forked from GNUsocial/gnu-social
[CORE][I18N] Fix the translation system
This commit is contained in:
parent
2b9a15c1e9
commit
2215b05894
@ -16,8 +16,13 @@
|
|||||||
|
|
||||||
namespace App\Util;
|
namespace App\Util;
|
||||||
|
|
||||||
class Common
|
abstract class Common
|
||||||
{
|
{
|
||||||
|
public static function config(string $section, string $field)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static function normalizePath(string $path): string
|
public static function normalizePath(string $path): string
|
||||||
{
|
{
|
||||||
if (DIRECTORY_SEPARATOR !== '/') {
|
if (DIRECTORY_SEPARATOR !== '/') {
|
||||||
|
@ -51,25 +51,31 @@
|
|||||||
|
|
||||||
namespace App\Util;
|
namespace App\Util;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
use App\Util\Log;
|
use App\Util\Log;
|
||||||
use App\Util\GSEvent;
|
use App\Util\GSEvent;
|
||||||
|
use App\Util\I18n;
|
||||||
|
|
||||||
class GNUsocial implements EventSubscriberInterface
|
class GNUsocial implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
protected ContainerInterface $container;
|
protected ContainerInterface $container;
|
||||||
protected LoggerInterface $logger;
|
protected LoggerInterface $logger;
|
||||||
|
protected TranslatorInterface $translator;
|
||||||
|
|
||||||
public function __construct(ContainerInterface $container, LoggerInterface $logger)
|
public function __construct(ContainerInterface $container,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
$this->logger= $logger;
|
$this->logger = $logger;
|
||||||
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onKernelRequest(RequestEvent $event,
|
public function onKernelRequest(RequestEvent $event,
|
||||||
@ -91,19 +97,21 @@ class GNUsocial implements EventSubscriberInterface
|
|||||||
define('GNUSOCIAL_LIFECYCLE', 'dev');
|
define('GNUSOCIAL_LIFECYCLE', 'dev');
|
||||||
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
|
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
|
||||||
define('GNUSOCIAL_CODENAME', 'Big bang');
|
define('GNUSOCIAL_CODENAME', 'Big bang');
|
||||||
|
|
||||||
|
/* Work internally in UTC */
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
/* Work internally with UTF-8 */
|
||||||
|
mb_internal_encoding('UTF-8');
|
||||||
|
|
||||||
|
Log::setLogger($this->logger);
|
||||||
|
GSEvent::setDispatcher($event_dispatcher);
|
||||||
|
I18n::setTranslator($this->translator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Work internally in UTC */
|
|
||||||
date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
/* Work internally with UTF-8 */
|
|
||||||
mb_internal_encoding('UTF-8');
|
|
||||||
|
|
||||||
Log::setLogger($this->logger);
|
|
||||||
GSEvent::setDispatcher($event_dispatcher);
|
|
||||||
|
|
||||||
GSEvent::addHandler('test', function ($x) {
|
GSEvent::addHandler('test', function ($x) {
|
||||||
Log::info("Logging from an event " . var_export($x, true));
|
Log::info(_m("Logging from an event " . var_export($x, true)));
|
||||||
});
|
});
|
||||||
|
|
||||||
return $event;
|
return $event;
|
||||||
|
@ -27,11 +27,7 @@
|
|||||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined('GNUSOCIAL') || die();
|
namespace App\Util;
|
||||||
|
|
||||||
private Translator $translator;
|
|
||||||
public function setTranslator($trans): void { $translator = $trans; }
|
|
||||||
|
|
||||||
|
|
||||||
// Locale category constants are usually predefined, but may not be
|
// Locale category constants are usually predefined, but may not be
|
||||||
// on some systems such as Win32.
|
// on some systems such as Win32.
|
||||||
@ -48,6 +44,45 @@ foreach ($LC_CATEGORIES as $key => $name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
use App\Util\Common;
|
||||||
|
|
||||||
|
abstract class I18n {
|
||||||
|
|
||||||
|
public static ?TranslatorInterface $translator = null;
|
||||||
|
public static function setTranslator($trans): void { self::$translator = $trans; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for which plugin we've been called from to set the gettext domain;
|
||||||
|
* if not in a plugin subdirectory, we'll use the default 'gnusocial'.
|
||||||
|
*
|
||||||
|
* @param array $backtrace debug_backtrace() output
|
||||||
|
* @return string
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
public static function _mdomain(array $backtrace): string
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
0 =>
|
||||||
|
array
|
||||||
|
'file' => string '/var/www/mublog/plugins/FeedSub/FeedSubPlugin.php' (length=49)
|
||||||
|
'line' => int 77
|
||||||
|
'function' => string '_m' (length=2)
|
||||||
|
'args' =>
|
||||||
|
array
|
||||||
|
0 => &string 'Feeds' (length=5)
|
||||||
|
*/
|
||||||
|
static $cached;
|
||||||
|
$path = $backtrace[0]['file'];
|
||||||
|
if (!isset($cached[$path])) {
|
||||||
|
$path = common::normalizePath($path);
|
||||||
|
$cached[$path] = common::pluginFromPath($path);
|
||||||
|
}
|
||||||
|
return $cached[$path] ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for symfony translation with smart domain detection.
|
* Wrapper for symfony translation with smart domain detection.
|
||||||
*
|
*
|
||||||
@ -68,91 +103,62 @@ foreach ($LC_CATEGORIES as $key => $name) {
|
|||||||
* @return string
|
* @return string
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function _m(string $msg /*, ...*/): string
|
function _m(string $msg /*, ...*/): string
|
||||||
{
|
{
|
||||||
$domain = _mdomain(debug_backtrace());
|
$domain = I18n::_mdomain(debug_backtrace());
|
||||||
$args = func_get_args();
|
$args = func_get_args();
|
||||||
list($context, $msg_single, $msg_plural, $n) = $args;
|
|
||||||
switch (count($args)) {
|
switch (count($args)) {
|
||||||
case 1:
|
case 1:
|
||||||
// Empty parameters
|
// Empty parameters
|
||||||
return $translator->trans($msg, [], $domain);
|
return I18n::$translator->trans($msg, [], $domain);
|
||||||
case 2:
|
case 2:
|
||||||
// ASCII 4 is EOT, used to separate context from string
|
$context = $args[0];
|
||||||
return $translator->trans($context . '\004' . $msg_single, [], $domain);
|
$msg_single = $args[1];
|
||||||
case 3:
|
// ASCII 4 is EOT, used to separate context from string
|
||||||
// '|' separates the singular from the plural version
|
return I18n::$translator->trans($context . '\004' . $msg_single, [], $domain);
|
||||||
$msg_single = $args[0];
|
case 3:
|
||||||
$msg_plural = $args[1];
|
// '|' separates the singular from the plural version
|
||||||
$n = $args[2];
|
$msg_single = $args[0];
|
||||||
return $translator->trans($msg_single . '|' . $msg_plural, ['%d' => $n], $domain);
|
$msg_plural = $args[1];
|
||||||
case 4:
|
$n = $args[2];
|
||||||
// Combine both
|
return I18n::$translator->trans($msg_single . '|' . $msg_plural, ['%d' => $n], $domain);
|
||||||
return $translator->trans($context . '\004' . $msg_single . '|' . $msg_plural,
|
case 4:
|
||||||
['%d' => $n], $domain);
|
// Combine both
|
||||||
default:
|
$context = $args[0];
|
||||||
throw new Exception("Bad parameter count to _m()");
|
$msg_single = $args[1];
|
||||||
|
$msg_plural = $args[2];
|
||||||
|
$n = $args[3];
|
||||||
|
return I18n::$translator->trans($context . '\004' . $msg_single . '|' . $msg_plural,
|
||||||
|
['%d' => $n], $domain);
|
||||||
|
default:
|
||||||
|
throw new Exception("Bad parameter count to _m()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks for which plugin we've been called from to set the gettext domain;
|
|
||||||
* if not in a plugin subdirectory, we'll use the default 'gnusocial'.
|
|
||||||
*
|
|
||||||
* Note: we can't return null for default domain since most of the PHP gettext
|
|
||||||
* wrapper functions turn null into "" before passing to the backend library.
|
|
||||||
*
|
|
||||||
* @param array $backtrace debug_backtrace() output
|
|
||||||
* @return string
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private function _mdomain(array $backtrace): string
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
0 =>
|
|
||||||
array
|
|
||||||
'file' => string '/var/www/mublog/plugins/FeedSub/FeedSubPlugin.php' (length=49)
|
|
||||||
'line' => int 77
|
|
||||||
'function' => string '_m' (length=2)
|
|
||||||
'args' =>
|
|
||||||
array
|
|
||||||
0 => &string 'Feeds' (length=5)
|
|
||||||
*/
|
|
||||||
static $cached;
|
|
||||||
$path = $backtrace[0]['file'];
|
|
||||||
if (!isset($cached[$path])) {
|
|
||||||
$path = common::normalizePath($path);
|
|
||||||
$cached[$path] = common::pluginFromPath($path);
|
|
||||||
}
|
|
||||||
return $cached[$path];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content negotiation for language codes
|
* Content negotiation for language codes
|
||||||
*
|
*
|
||||||
* @param string $http_accept_lang_header HTTP Accept-Language header
|
* @param string $http_accept_lang_header HTTP Accept-Language header
|
||||||
* @return string language code for best language match, false otherwise
|
* @return string language code for best language match, false otherwise
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function client_preferred_language(string $http_accept_lang_header): string
|
function client_preferred_language(string $http_accept_lang_header): string
|
||||||
{
|
{
|
||||||
$client_langs = [];
|
$client_langs = [];
|
||||||
$all_languages = common_config('site', 'languages');
|
$all_languages = Common::config('site', 'languages');
|
||||||
|
|
||||||
preg_match_all('"(((\S\S)-?(\S\S)?)(;q=([0-9.]+))?)\s*(,\s*|$)"',
|
preg_match_all('"(((\S\S)-?(\S\S)?)(;q=([0-9.]+))?)\s*(,\s*|$)"',
|
||||||
strtolower($http_accept_lang_header), $http_langs);
|
strtolower($http_accept_lang_header), $http_langs);
|
||||||
|
|
||||||
for ($i = 0; $i < count($http_langs); ++$i) {
|
for ($i = 0; $i < count($http_langs); ++$i) {
|
||||||
if (!empty($http_langs[2][$i])) {
|
if (!empty($http_langs[2][$i])) {
|
||||||
// if no q default to 1.0
|
// if no q default to 1.0
|
||||||
$client_langs[$http_langs[2][$i]] =
|
$client_langs[$http_langs[2][$i]] =
|
||||||
($http_langs[6][$i] ? (float)$http_langs[6][$i] : 1.0 - ($i * 0.01));
|
($http_langs[6][$i] ? (float)$http_langs[6][$i] : 1.0 - ($i * 0.01));
|
||||||
}
|
}
|
||||||
if (!empty($http_langs[3][$i]) && empty($client_langs[$http_langs[3][$i]])) {
|
if (!empty($http_langs[3][$i]) && empty($client_langs[$http_langs[3][$i]])) {
|
||||||
// if a catchall default 0.01 lower
|
// if a catchall default 0.01 lower
|
||||||
$client_langs[$http_langs[3][$i]] =
|
$client_langs[$http_langs[3][$i]] =
|
||||||
($http_langs[6][$i] ? (float)$http_langs[6][$i] - 0.01 : 0.99);
|
($http_langs[6][$i] ? (float)$http_langs[6][$i] - 0.01 : 0.99);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sort in descending q
|
// sort in descending q
|
||||||
@ -193,20 +199,16 @@ function get_nice_language_list(): array
|
|||||||
|
|
||||||
function is_rtl(string $lang_value): bool
|
function is_rtl(string $lang_value): bool
|
||||||
{
|
{
|
||||||
foreach (common_config('site', 'languages') as $code => $info) {
|
foreach (common_config('site', 'languages') as $code => $info) {
|
||||||
if ($lang_value == $info['lang']) {
|
if ($lang_value == $info['lang']) {
|
||||||
return $info['direction'] == 'rtl';
|
return $info['direction'] == 'rtl';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all languages that are enabled in the default config
|
* Get a list of all languages that are enabled in the default config
|
||||||
*
|
*
|
||||||
* This should ONLY be called when setting up the default config in common.php.
|
|
||||||
* Any other attempt to get a list of languages should instead call
|
|
||||||
* common_config('site','languages')
|
|
||||||
*
|
|
||||||
* @return array mapping of language codes to language info
|
* @return array mapping of language codes to language info
|
||||||
*/
|
*/
|
||||||
function get_all_languages(): array
|
function get_all_languages(): array
|
Loading…
Reference in New Issue
Block a user