‪TYPO3CMS  11.5
LanguagePackService.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
21 use Psr\Log\LoggerInterface;
22 use Symfony\Component\Finder\Finder;
28 use TYPO3\CMS\Core\Package\PackageManager;
35 
43 {
47  protected ‪$locales;
48 
52  protected ‪$registry;
53 
57  protected ‪$eventDispatcher;
58 
62  protected ‪$requestFactory;
63 
67  protected ‪$logger;
68 
69  private const ‪LANGUAGE_PACK_URL = 'https://localize.typo3.org/xliff/';
70 
71  public function ‪__construct(
72  EventDispatcherInterface ‪$eventDispatcher,
74  LoggerInterface ‪$logger
75  ) {
76  $this->eventDispatcher = ‪$eventDispatcher;
77  $this->locales = GeneralUtility::makeInstance(Locales::class);
78  $this->registry = GeneralUtility::makeInstance(Registry::class);
79  $this->requestFactory = ‪$requestFactory;
80  $this->logger = ‪$logger;
81  }
82 
88  public function ‪getAvailableLanguages(): array
89  {
90  return $this->locales->getLanguages();
91  }
92 
98  public function ‪getActiveLanguages(): array
99  {
100  $availableLanguages = ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lang']['availableLanguages'] ?? [];
101  return array_filter($availableLanguages);
102  }
103 
109  public function ‪getLanguageDetails(): array
110  {
111  $availableLanguages = $this->‪getAvailableLanguages();
112  $activeLanguages = $this->‪getActiveLanguages();
113  $languages = [];
114  foreach ($availableLanguages as $iso => $name) {
115  if ($iso === 'default') {
116  continue;
117  }
118  $lastUpdate = $this->registry->get('languagePacks', $iso);
119  $languages[] = [
120  'iso' => $iso,
121  'name' => $name,
122  'active' => in_array($iso, $activeLanguages, true),
123  'lastUpdate' => $this->‪getFormattedDate($lastUpdate),
124  'dependencies' => $this->locales->getLocaleDependencies($iso),
125  ];
126  }
127  usort($languages, static function ($a, $b) {
128  // Sort languages by name
129  if ($a['name'] === $b['name']) {
130  return 0;
131  }
132  return $a['name'] < $b['name'] ? -1 : 1;
133  });
134  return $languages;
135  }
136 
142  public function ‪getExtensionLanguagePackDetails(): array
143  {
144  $activeLanguages = $this->‪getActiveLanguages();
145  $packageManager = GeneralUtility::makeInstance(PackageManager::class);
146  $activePackages = $packageManager->getActivePackages();
147  $extensions = [];
148  foreach ($activePackages as $package) {
149  $path = $package->getPackagePath();
150  ‪$finder = new Finder();
151  try {
152  $files = ‪$finder->files()->in($path . 'Resources/Private/Language/')->name('*.xlf');
153  if ($files->count() === 0) {
154  // This extension has no .xlf files
155  continue;
156  }
157  } catch (\InvalidArgumentException $e) {
158  // Dir does not exist
159  continue;
160  }
161  $key = $package->getPackageKey();
162  $title = $package->getValueFromComposerManifest('description') ?? '';
163  if (is_file($path . 'ext_emconf.php')) {
164  $_EXTKEY = $key;
165  ‪$EM_CONF = [];
166  include $path . 'ext_emconf.php';
167  $title = ‪$EM_CONF[$key]['title'] ?? $title;
168 
169  $state = ‪$EM_CONF[$key]['state'] ?? '';
170  if ($state === 'excludeFromUpdates') {
171  continue;
172  }
173  }
174  $extension = [
175  'key' => $key,
176  'title' => $title,
177  ];
178  if (!empty(‪ExtensionManagementUtility::getExtensionIcon($path, false))) {
180  }
181  $extension['packs'] = [];
182  foreach ($activeLanguages as $iso) {
183  $isLanguagePackDownloaded = is_dir(‪Environment::getLabelsPath() . '/' . $iso . '/' . $key . '/');
184  $lastUpdate = $this->registry->get('languagePacks', $iso . '-' . $key);
185  $extension['packs'][] = [
186  'iso' => $iso,
187  'exists' => $isLanguagePackDownloaded,
188  'lastUpdate' => $this->‪getFormattedDate($lastUpdate),
189  ];
190  }
191  $extensions[] = $extension;
192  }
193  usort($extensions, static function ($a, $b) {
194  // Sort extensions by key
195  if ($a['key'] === $b['key']) {
196  return 0;
197  }
198  return $a['key'] < $b['key'] ? -1 : 1;
199  });
200  return $extensions;
201  }
202 
211  public function ‪languagePackDownload(string $key, string $iso): string
212  {
213  // Sanitize extension and iso code
214  $availableLanguages = $this->‪getAvailableLanguages();
215  $activeLanguages = $this->‪getActiveLanguages();
216  if (!array_key_exists($iso, $availableLanguages) || !in_array($iso, $activeLanguages, true)) {
217  throw new \RuntimeException('Language iso code ' . (string)$iso . ' not available or active', 1520117054);
218  }
219  $packageManager = GeneralUtility::makeInstance(PackageManager::class);
220  $package = $packageManager->getActivePackages()[$key] ?? null;
221  if (!$package) {
222  throw new \RuntimeException('Extension ' . (string)$key . ' not loaded', 1520117245);
223  }
224 
225  $languagePackBaseUrl = ‪self::LANGUAGE_PACK_URL;
226 
227  // Allow to modify the base url on the fly
228  $event = $this->eventDispatcher->dispatch(new ModifyLanguagePackRemoteBaseUrlEvent(new Uri($languagePackBaseUrl), $key));
229  $languagePackBaseUrl = $event->getBaseUrl();
230  $majorVersion = GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion();
231  if ($package->getPackageMetaData()->isFrameworkType()) {
232  // This is a system extension and the package URL should be adapted to have different packs per core major version
233  // https://localize.typo3.org/xliff/b/a/backend-l10n/backend-l10n-fr.v9.zip
234  $packageUrl = $key[0] . '/' . $key[1] . '/' . $key . '-l10n/' . $key . '-l10n-' . $iso . '.v' . $majorVersion . '.zip';
235  } else {
236  // Typical non sysext path, Hungarian:
237  // https://localize.typo3.org/xliff/a/n/anextension-l10n/anextension-l10n-hu.zip
238  $packageUrl = $key[0] . '/' . $key[1] . '/' . $key . '-l10n/' . $key . '-l10n-' . $iso . '.zip';
239  }
240 
241  $absoluteLanguagePath = ‪Environment::getLabelsPath() . '/' . $iso . '/';
242  $absoluteExtractionPath = $absoluteLanguagePath . $key . '/';
243  $absolutePathToZipFile = ‪Environment::getVarPath() . '/transient/' . $key . '-l10n-' . $iso . '.zip';
244 
245  $packExists = is_dir($absoluteExtractionPath);
246 
247  $packResult = $packExists ? 'update' : 'new';
248 
249  $operationResult = false;
250 
251  $response = $this->requestFactory->request($languagePackBaseUrl . $packageUrl, 'GET', ['http_errors' => false]);
252  if ($response->getStatusCode() === 200) {
253  $languagePackContent = $response->getBody()->getContents();
254  if (!empty($languagePackContent)) {
255  $operationResult = true;
256  if ($packExists) {
257  $operationResult = ‪GeneralUtility::rmdir($absoluteExtractionPath, true);
258  }
259  if ($operationResult) {
261  $operationResult = ‪GeneralUtility::writeFileToTypo3tempDir($absolutePathToZipFile, $languagePackContent) === null;
262  }
263  $this->‪unzipTranslationFile($absolutePathToZipFile, $absoluteLanguagePath);
264  if ($operationResult) {
265  $operationResult = unlink($absolutePathToZipFile);
266  }
267  }
268  } else {
269  $operationResult = false;
270 
271  $this->logger->warning('Requesting {request} was not successful, got status code {status} ({reason})', [
272  'request' => $languagePackBaseUrl . $packageUrl,
273  'status' => $response->getStatusCode(),
274  'reason' => $response->getReasonPhrase(),
275  ]);
276  }
277  if (!$operationResult) {
278  $packResult = 'failed';
279  $this->registry->set('languagePacks', $iso . '-' . $key, time());
280  }
281  return $packResult;
282  }
283 
290  public function ‪setLastUpdatedIsoCode(array $isos)
291  {
292  $activeLanguages = ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lang']['availableLanguages'] ?? [];
293  foreach ($isos as $iso) {
294  if (!in_array($iso, $activeLanguages, true)) {
295  throw new \RuntimeException('Language iso code ' . (string)$iso . ' not available or active', 1520176318);
296  }
297  $this->registry->set('languagePacks', $iso, time());
298  }
299  }
300 
307  protected function ‪getFormattedDate($timestamp)
308  {
309  if (is_int($timestamp)) {
310  $date = (new \DateTime())->setTimestamp($timestamp);
311  $format = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
312  $timestamp = $date->format($format);
313  }
314  return $timestamp;
315  }
316 
323  protected function ‪unzipTranslationFile(string $file, string $path)
324  {
325  if (!is_dir($path)) {
327  }
328 
329  $zipService = GeneralUtility::makeInstance(ZipService::class);
330  if ($zipService->verify($file)) {
331  $zipService->extract($file, $path);
332  }
334  }
335 }
‪TYPO3\CMS\Install\Service\LanguagePackService\getFormattedDate
‪string null getFormattedDate($timestamp)
Definition: LanguagePackService.php:302
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪$finder
‪if(PHP_SAPI !=='cli') $finder
Definition: header-comment.php:22
‪$EM_CONF
‪$EM_CONF[$_EXTKEY]
Definition: ext_emconf.php:3
‪TYPO3\CMS\Core\Core\Environment\getLabelsPath
‪static string getLabelsPath()
Definition: Environment.php:262
‪TYPO3\CMS\Core\Information\Typo3Version
Definition: Typo3Version.php:21
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:33
‪TYPO3\CMS\Core\Localization\Locales
Definition: Locales.php:30
‪TYPO3\CMS\Install\Service\LanguagePackService\getActiveLanguages
‪array getActiveLanguages()
Definition: LanguagePackService.php:93
‪TYPO3\CMS\Install\Service\LanguagePackService\languagePackDownload
‪string languagePackDownload(string $key, string $iso)
Definition: LanguagePackService.php:206
‪TYPO3\CMS\Install\Service\LanguagePackService\LANGUAGE_PACK_URL
‪const LANGUAGE_PACK_URL
Definition: LanguagePackService.php:64
‪TYPO3\CMS\Core\Http\Uri
Definition: Uri.php:29
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Install\Service\LanguagePackService\setLastUpdatedIsoCode
‪setLastUpdatedIsoCode(array $isos)
Definition: LanguagePackService.php:285
‪TYPO3\CMS\Install\Service\LanguagePackService\unzipTranslationFile
‪unzipTranslationFile(string $file, string $path)
Definition: LanguagePackService.php:318
‪TYPO3\CMS\Install\Service\LanguagePackService\__construct
‪__construct(EventDispatcherInterface $eventDispatcher, RequestFactory $requestFactory, LoggerInterface $logger)
Definition: LanguagePackService.php:66
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixPermissions
‪static mixed fixPermissions($path, $recursive=false)
Definition: GeneralUtility.php:1749
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:1908
‪TYPO3\CMS\Install\Service\LanguagePackService\$locales
‪Locales $locales
Definition: LanguagePackService.php:46
‪TYPO3\CMS\Install\Service\LanguagePackService\$logger
‪LoggerInterface $logger
Definition: LanguagePackService.php:62
‪TYPO3\CMS\Core\Http\RequestFactory
Definition: RequestFactory.php:31
‪TYPO3\CMS\Install\Service\LanguagePackService
Definition: LanguagePackService.php:43
‪TYPO3\CMS\Core\Service\Archive\ZipService
Definition: ZipService.php:29
‪TYPO3\CMS\Install\Service\LanguagePackService\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: LanguagePackService.php:54
‪TYPO3\CMS\Install\Service\LanguagePackService\getExtensionLanguagePackDetails
‪array getExtensionLanguagePackDetails()
Definition: LanguagePackService.php:137
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:43
‪TYPO3\CMS\Install\Service\Event\ModifyLanguagePackRemoteBaseUrlEvent
Definition: ModifyLanguagePackRemoteBaseUrlEvent.php:26
‪TYPO3\CMS\Core\Utility\GeneralUtility\rmdir
‪static bool rmdir($path, $removeNonEmpty=false)
Definition: GeneralUtility.php:1961
‪TYPO3\CMS\Install\Service\LanguagePackService\getAvailableLanguages
‪array getAvailableLanguages()
Definition: LanguagePackService.php:83
‪TYPO3\CMS\Install\Service\LanguagePackService\$registry
‪Registry $registry
Definition: LanguagePackService.php:50
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\getExtensionIcon
‪static string getExtensionIcon($extensionPath, $returnFullPath=false)
Definition: ExtensionManagementUtility.php:1482
‪TYPO3\CMS\Core\Utility\PathUtility\getAbsoluteWebPath
‪static string getAbsoluteWebPath($targetPath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:51
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Install\Service\LanguagePackService\getLanguageDetails
‪array getLanguageDetails()
Definition: LanguagePackService.php:104
‪TYPO3\CMS\Install\Service
Definition: ClearCacheService.php:16
‪TYPO3\CMS\Install\Service\LanguagePackService\$requestFactory
‪RequestFactory $requestFactory
Definition: LanguagePackService.php:58
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:218
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFileToTypo3tempDir
‪static string null writeFileToTypo3tempDir($filepath, $content)
Definition: GeneralUtility.php:1814