Commit c0098853 authored by Johannes Kasberger's avatar Johannes Kasberger 🦀 Committed by Stefan Busemann

Show crowdin translation status

parent 68dc31ca
<?php
namespace T3o\TerFe2\Command;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use T3o\TerFe2\Service\TranslationStatusService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class FetchTranslationStatusCommand extends Command
{
protected function configure()
{
$this->setDescription('Fetches the current translation status for extensions that use crowdin');
}
/**
* Executes the command for showing sys_log entries
*
* @param InputInterface $input
* @param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$translationService = GeneralUtility::makeInstance(TranslationStatusService::class);
if ($translationService->updateStatus()) {
$io->success('Translation status has been updated');
} else {
$io->error('Error during update of translation status');
foreach ($translationService->getMessages() as $message) {
$io->error($message);
}
}
}
}
......@@ -131,6 +131,16 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
*/
protected $notifications = 0;
/**
* @var string
*/
protected $localizationStatus = '';
/**
* @var string
*/
protected $crowdinKey = '';
/**
* Initialize all ObjectStorage instances.
*/
......@@ -657,4 +667,39 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
return $supportedTypo3Versions;
}
/**
* @return array
*/
public function getLocalizationStatus(): array
{
if (!empty($this->localizationStatus)) {
return json_decode($this->localizationStatus, true);
}
return [];
}
/**
* @param string $localizationStatus
*/
public function setLocalizationStatus(string $localizationStatus)
{
$this->localizationStatus = $localizationStatus;
}
/**
* @return string
*/
public function getCrowdinKey(): string
{
return $this->crowdinKey;
}
/**
* @param string $crowdinKey
*/
public function setCrowdinKey(string $crowdinKey)
{
$this->crowdinKey = $crowdinKey;
}
}
<?php
namespace T3o\TerFe2\Service;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use T3o\TerFe2\Domain\Model\Extension;
use T3o\TerFe2\Domain\Repository\ExtensionRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException;
use TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
class TranslationStatusService
{
/** @var string */
private $statusUrl = 'https://localize.typo3.org/fileadmin/ter/status.json';
/** @var ObjectManager */
private $objectManager;
/** @var ExtensionRepository */
private $extensionRepository;
/** @var array */
private $messages = [];
/** @var array */
private $languageNames = [];
public function __construct()
{
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->extensionRepository = $this->objectManager->get(ExtensionRepository::class);
$querySettings = GeneralUtility::makeInstance(Typo3QuerySettings::class);
$querySettings->setRespectStoragePage(false);
$this->extensionRepository->setDefaultQuerySettings($querySettings);
}
/**
* @return bool
*/
public function updateStatus(): bool
{
$jsonStatus = GeneralUtility::getUrl($this->statusUrl);
if ($jsonStatus === false) {
$this->messages[] = 'Error during fetch of ' . $this->statusUrl;
return false;
}
$fetchedData = json_decode($jsonStatus, true);
$this->languageNames = $fetchedData['languages'];
foreach ($fetchedData['projects'] as $extensionStatus) {
if (empty($extensionStatus['extensionKey'])) {
continue;
}
/** @var Extension $extension */
$extension = $this->extensionRepository->findOneByExtKey($extensionStatus['extensionKey']);
if (!$extension) {
$this->messages[] = 'Ignoring Extension key ' . $extensionStatus['extensionKey'] . ' since key is not found in extensions list';
continue;
}
$extension->setCrowdinKey($extensionStatus['crowdinKey']);
if ($extensionStatus['usable']) {
$activeLanguages = $this->getActiveLanguages($extensionStatus['languages']);
} else {
$activeLanguages = [];
}
$extension->setLocalizationStatus(json_encode($activeLanguages));
$errorDuringUpdate = false;
try {
$this->extensionRepository->update($extension);
} catch (IllegalObjectTypeException $e) {
$errorDuringUpdate = true;
} catch (UnknownObjectException $e) {
$errorDuringUpdate = true;
}
if ($errorDuringUpdate) {
$this->messages[] = 'Error during update for ' . $extensionStatus['extensionKey'];
}
}
/** @var PersistenceManager $persistenceManager */
$persistenceManager = $this->objectManager->get(PersistenceManager::class);
$persistenceManager->persistAll();
return count($this->messages) === 0;
}
/**
* Returns all active languages for the extension
*
* @param array $languages
* @return array
*/
private function getActiveLanguages(array $languages): array
{
$activeLanguages = [];
arsort($languages);
foreach ($languages as $language => $value) {
if ($value === '-') {
continue;
}
$percentage = (int)$value;
if ($percentage <= 0 || $percentage > 100) {
continue;
}
$activeLanguages[] = ['iso' => $language, 'percentage' => $percentage, 'name' => $this->languageNames[$language]];
}
return $activeLanguages;
}
/**
* @return array
*/
public function getMessages(): array
{
return $this->messages;
}
}
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
return [
'ter_fe2:fetchTranslationStatus' => [
'class' => \T3o\TerFe2\Command\FetchTranslationStatusCommand::class
],
];
......@@ -19,10 +19,10 @@ return [
'iconfile' => \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ter_fe2')) . 'Resources/Public/Icons/extension.gif',
],
'interface' => [
'showRecordFieldList' => 'ext_key,forge_link,last_update,last_maintained,tags,versions,last_version,frontend_user,downloads,composer_name,repository_url,paypal_url,external_manual,expire,security_team_notice',
'showRecordFieldList' => 'ext_key,forge_link,last_update,last_maintained,tags,versions,last_version,frontend_user,downloads,composer_name,repository_url,paypal_url,external_manual,expire,security_team_notice,localization_status,crowdin_key',
],
'types' => [
'1' => ['showitem' => 'ext_key,forge_link,last_update,last_maintained,tags,versions,last_version,frontend_user,downloads,composer_name,repository_url,paypal_url,external_manual,security_team_notice,expire'],
'1' => ['showitem' => 'ext_key,forge_link,last_update,last_maintained,tags,versions,last_version,frontend_user,downloads,composer_name,repository_url,paypal_url,external_manual,security_team_notice,expire,localization_status,crowdin_key'],
],
'palettes' => [
'1' => ['showitem' => ''],
......@@ -255,6 +255,24 @@ return [
'eval' => 'trim',
],
],
'localization_status' => [
'exclude' => 1,
'label' => 'LLL:EXT:ter_fe2/Resources/Private/Language/locallang_db.xlf:tx_terfe2_domain_model_extension.localization_status',
'config' => [
'type' => 'text',
'rows' => 10,
'cols' => 40,
'readOnly' => true
],
],
'crowdin_key' => [
'exclude' => 1,
'label' => 'LLL:EXT:ter_fe2/Resources/Private/Language/locallang_db.xlf:tx_terfe2_domain_model_extension.crowdin_key',
'config' => [
'type' => 'input',
'size' => 30,
'readOnly' => true
],
]
],
];
......@@ -54,6 +54,12 @@
<trans-unit id="tx_terfe2_domain_model_extension.expire" xml:space="preserve">
<source>Extension key expires at</source>
</trans-unit>
<trans-unit id="tx_terfe2_domain_model_extension.localization_status" xml:space="preserve">
<source>Localization status as json</source>
</trans-unit>
<trans-unit id="tx_terfe2_domain_model_extension.crowdin_key" xml:space="preserve">
<source>Crowdin Key</source>
</trans-unit>
<trans-unit id="tx_terfe2_domain_model_tag" xml:space="preserve">
<source>Tag</source>
</trans-unit>
......
......@@ -102,6 +102,17 @@
<f:render partial="ComposerNameWithClipboard" arguments="{composerName: extension.composerName}" />
</p>
</f:if>
<f:if condition="{extension.localizationStatus}">
<h4>Localization Status</h4>
<p class="tags">
<f:for each="{extension.localizationStatus}" as="localization">
<a href="https://crowdin.com/project/{extension.crowdinKey}/{localization.iso}" class="btn btn-outline-info mb-1" target="_blank" rel="noopener noreferrer">
{localization.name}: {localization.percentage}%
</a>
</f:for>
</p>
<p><a href="https://crowdin.com/project/{extension.crowdinKey}/invite" target="_blank" rel="noopener noreferrer">Crowdin translations for this extension</a></p>
</f:if>
<f:if condition="{extension.tags}">
<h4>Tags</h4>
<p class="tags">
......
......@@ -22,6 +22,8 @@ CREATE TABLE tx_terfe2_domain_model_extension (
likes int(11) unsigned default '0' NOT NULL,
notifications int(11) unsigned default '0' NOT NULL,
security_team_notice text,
crowdin_key varchar(255) DEFAULT '' NOT NULL,
localization_status mediumtext,
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
crdate int(11) unsigned DEFAULT '0' NOT NULL,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment