162 lines
6.9 KiB
PHP
162 lines
6.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* 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!
|
|
*/
|
|
|
|
namespace TYPO3\CMS\Extbase\Utility;
|
|
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
|
|
use TYPO3\CMS\Core\Localization\Locale;
|
|
use TYPO3\CMS\Core\Localization\Locales;
|
|
use TYPO3\CMS\Core\Localization\TranslationDomainMapper;
|
|
use TYPO3\CMS\Core\Localization\TranslationDomainResolver;
|
|
use TYPO3\CMS\Core\TypoScript\FrontendTypoScript;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
use TYPO3\CMS\Extbase\Mvc\Request;
|
|
|
|
/**
|
|
* Localization helper which should be used to fetch localized labels.
|
|
*/
|
|
class LocalizationUtility
|
|
{
|
|
/**
|
|
* Returns the localized label of the LOCAL_LANG key, $key.
|
|
*
|
|
* @param string $key The key from the LOCAL_LANG array for which to return the value.
|
|
* @param string|null $extensionName The name of the extension or domain name (such as "myextension.messages")
|
|
* @param array|null $arguments The arguments for the label. For sprintf-style labels (e.g., "Downloaded %d times"),
|
|
* use positional arguments [42]. For ICU MessageFormat labels
|
|
* (e.g., "{count, plural, one {# file} other {# files}}"), use named arguments ['count' => 42].
|
|
* @param Locale|string|null $languageKey The language key or null for using the current language from the system
|
|
* @return string|null The value from LOCAL_LANG or null if no translation was found.
|
|
*/
|
|
public static function translate(string $key, ?string $extensionName = null, ?array $arguments = null, Locale|string|null $languageKey = null, ?ServerRequestInterface $request = null): ?string
|
|
{
|
|
if ($key === '') {
|
|
// Early return guard: returns null if the key was empty, because the key may be a dynamic value
|
|
// (from for example Fluid). Returning null allows null coalescing to a default value when that happens.
|
|
return null;
|
|
}
|
|
|
|
$languageFilePath = null;
|
|
if (str_starts_with($key, 'LLL:EXT:')) {
|
|
$keyParts = explode(':', $key);
|
|
unset($keyParts[0]);
|
|
$key = array_pop($keyParts);
|
|
$languageFilePath = implode(':', $keyParts);
|
|
} elseif ($extensionName) {
|
|
if (str_contains($extensionName, '.')) {
|
|
// We assume this is a valid domain now
|
|
$languageFilePath = $extensionName;
|
|
$extensionName = self::getExtensionNameFromDomain($languageFilePath);
|
|
} else {
|
|
$languageFilePath = GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '.messages';
|
|
}
|
|
} elseif (str_contains($key, ':')) {
|
|
[$languageFilePath, $key, $extensionName] = self::extractLanguageInfoFromDomainString($key);
|
|
}
|
|
if ($languageFilePath === null || $key === '') {
|
|
throw new \InvalidArgumentException(
|
|
'Parameter $extensionName cannot be empty if a fully-qualified key is not specified.',
|
|
1498144052
|
|
);
|
|
}
|
|
$request = $request ?? $GLOBALS['TYPO3_REQUEST'] ?? null;
|
|
$locale = self::getLocale($languageKey, $request);
|
|
$languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create($locale);
|
|
if (!empty($extensionName) && $request instanceof ServerRequestInterface) {
|
|
$typoScript = $request->getAttribute('frontend.typoscript');
|
|
if ($typoScript instanceof FrontendTypoScript) {
|
|
// Loads local-language values by looking for a "locallang.xlf" file in the plugin resources directory and if found includes it.
|
|
// Locallang values set in the TypoScript property "_LOCAL_LANG" are merged onto the values found in the "locallang.xlf" file.
|
|
$overrideLabels = $languageService->loadTypoScriptLabelsFromExtension($extensionName, $typoScript, self::getPluginName($request));
|
|
if ($overrideLabels !== []) {
|
|
$languageService->overrideLabels($languageFilePath, $overrideLabels);
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
return $languageService->translate($key, $languageFilePath, $arguments ?? []);
|
|
} catch (\ValueError $e) {
|
|
return sprintf('Error: could not translate key "%s" with value "%s" and %d argument(s)!', $key, $e->getMessage(), count($arguments));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resolves the currently active locale.
|
|
* Using the Locales factory, as it handles dependencies (e.g. "de-AT" falls back to "de").
|
|
*/
|
|
protected static function getLocale(Locale|string|null $localeOrLanguageKey, ?ServerRequestInterface $request): Locale
|
|
{
|
|
if ($localeOrLanguageKey instanceof Locale) {
|
|
return $localeOrLanguageKey;
|
|
}
|
|
$localeFactory = GeneralUtility::makeInstance(Locales::class);
|
|
if (is_string($localeOrLanguageKey)) {
|
|
return $localeFactory->createLocale($localeOrLanguageKey);
|
|
}
|
|
return $localeFactory->createLocaleFromRequest($request);
|
|
}
|
|
|
|
/**
|
|
* Allow plugin.tx_myextension._LOCAL_LANG and plugin.tx_myextension_myplugin._LOCAL_LANG
|
|
*/
|
|
protected static function getPluginName(?ServerRequestInterface $request): string
|
|
{
|
|
if ($request instanceof Request) {
|
|
return strtolower($request->getPluginName());
|
|
}
|
|
return '';
|
|
}
|
|
|
|
private static function getExtensionNameFromDomain(string $possibleDomain): ?string
|
|
{
|
|
[$extensionName, $filePart] = explode('.', $possibleDomain, 2);
|
|
if ($filePart !== 'messages') {
|
|
return null;
|
|
}
|
|
return $extensionName;
|
|
}
|
|
|
|
/**
|
|
* @return array<?string>
|
|
*/
|
|
private static function extractLanguageInfoFromDomainString(string $key): array
|
|
{
|
|
$languageFilePath = null;
|
|
$extensionName = null;
|
|
|
|
if (str_starts_with($key, 'LLL:')) {
|
|
$key = substr($key, 4);
|
|
}
|
|
|
|
[$possibleDomain, $possibleId] = explode(':', $key, 2);
|
|
$domainMapper = GeneralUtility::makeInstance(TranslationDomainMapper::class);
|
|
$domainResolver = GeneralUtility::makeInstance(TranslationDomainResolver::class);
|
|
if ($domainResolver->isValidDomainName($possibleDomain) && $domainMapper->mapDomainToFileName($possibleDomain) !== $possibleDomain) {
|
|
$languageFilePath = $possibleDomain;
|
|
$key = trim($possibleId);
|
|
$extensionName = self::getExtensionNameFromDomain($languageFilePath);
|
|
}
|
|
return [
|
|
$languageFilePath,
|
|
$key,
|
|
$extensionName,
|
|
];
|
|
}
|
|
}
|