[I18N] Refactor since rfc/use-static-function is not implemented

As the above mentioned RFC is not implemented, `_m` needs to be
outside of the I18n class, otherwise it would have to always be called
with `I18n::_m`.
This commit is contained in:
Hugo Sales 2020-06-04 21:53:34 +00:00 committed by Hugo Sales
parent 186b9e7683
commit f283613443
Signed by: someonewithpc
GPG Key ID: 7D0C7EAFC9D835A0
5 changed files with 136 additions and 98 deletions

View File

@ -33,7 +33,7 @@ namespace App\Controller;
// use App\Core\GSEvent as Event; // use App\Core\GSEvent as Event;
// use App\Util\Common; // use App\Util\Common;
use App\Core\DB\DefaultSettings; use App\Core\DB\DefaultSettings;
use App\Core\I18n; use function App\Core\I18n\_m;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@ -48,14 +48,14 @@ class AdminConfigController extends AbstractController
foreach (DefaultSettings::$defaults as $key => $inner) { foreach (DefaultSettings::$defaults as $key => $inner) {
$options[$key] = []; $options[$key] = [];
foreach (array_keys($inner) as $inner_key) { foreach (array_keys($inner) as $inner_key) {
$options[I18n::_m($key)][I18n::_m($inner_key)] = "{$key}:{$inner_key}"; $options[_m($key)][_m($inner_key)] = "{$key}:{$inner_key}";
} }
} }
$form = $this->createFormBuilder() $form = $this->createFormBuilder(null, ['translation_domain' => false])
->add(I18n::_m('Setting'), ChoiceType::class, ['choices' => $options]) ->add(_m('Setting'), ChoiceType::class, ['choices' => $options])
->add(I18n::_m('Value'), TextType::class) ->add(_m('Value'), TextType::class)
->add('save', SubmitType::class, ['label' => I18n::_m('Set site setting')]) ->add('save', SubmitType::class, ['label' => _m('Set site setting')])
->getForm(); ->getForm();
$form->handleRequest($request); $form->handleRequest($request);

View File

@ -32,7 +32,7 @@
namespace App\Core\DB; namespace App\Core\DB;
use App\Core\I18n; use App\Core\I18n\I18nHelper;
use App\Util\Common; use App\Util\Common;
abstract class DefaultSettings abstract class DefaultSettings
@ -47,7 +47,7 @@ abstract class DefaultSettings
'logo' => null, 'logo' => null,
'language' => 'en', 'language' => 'en',
'detect_language' => true, 'detect_language' => true,
'languages' => I18n::get_all_languages(), 'languages' => I18nHelper::get_all_languages(),
'email' => $_ENV['SERVER_ADMIN'] ?? $_ENV['SOCIAL_ADMIN_EMAIL'] ?? null, 'email' => $_ENV['SERVER_ADMIN'] ?? $_ENV['SOCIAL_ADMIN_EMAIL'] ?? null,
'recovery_disclose' => false, // Whether to not say that we found the email in the database, when asking for recovery 'recovery_disclose' => false, // Whether to not say that we found the email in the database, when asking for recovery
'timezone' => 'UTC', 'timezone' => 'UTC',

View File

@ -42,6 +42,7 @@ namespace App\Core;
use App\Core\DB\DB; use App\Core\DB\DB;
use App\Core\DB\DefaultSettings; use App\Core\DB\DefaultSettings;
use App\Core\I18n\I18nHelper;
use App\Core\Router\Router; use App\Core\Router\Router;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
@ -87,7 +88,7 @@ class GNUsocial implements EventSubscriberInterface
{ {
Log::setLogger($this->logger); Log::setLogger($this->logger);
GSEvent::setDispatcher($event_dispatcher); GSEvent::setDispatcher($event_dispatcher);
I18n::setTranslator($this->translator); I18nHelper::setTranslator($this->translator);
DB::setManager($this->entity_manager); DB::setManager($this->entity_manager);
Router::setRouter($this->router); Router::setRouter($this->router);

115
src/Core/I18n/I18n.php Normal file
View File

@ -0,0 +1,115 @@
<?php
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* Utility functions for i18n
*
* @category I18n
* @package GNU social
*
* @author Matthew Gregg <matthew.gregg@gmail.com>
* @author Ciaran Gultnieks <ciaran@ciarang.com>
* @author Evan Prodromou <evan@status.net>
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Hugo Sales <hugo@fc.up.pt>
* @copyright 2010, 2018-2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace App\Core\I18n;
// Locale category constants are usually predefined, but may not be
// on some systems such as Win32.
$LC_CATEGORIES = [
'LC_CTYPE',
'LC_NUMERIC',
'LC_TIME',
'LC_COLLATE',
'LC_MONETARY',
'LC_MESSAGES',
'LC_ALL',
];
foreach ($LC_CATEGORIES as $key => $name) {
if (!defined($name)) {
define($name, $key);
}
}
abstract class I18n
{
// Dummy class, because the bellow function needs to be outside of
// a class, since `rfc/use-static-function` isn't implemented, so
// we'd have to use `I18n::_m`, but the autoloader still needs a class with the same name as the file
}
/**
* Wrapper for symfony translation with smart domain detection.
*
* If calling from a plugin, this function checks which plugin it was
* being called from and uses that as text domain, which will have
* been set up during plugin initialization.
*
* Also handles plurals and contexts depending on what parameters
* are passed to it:
*
* _m($msg) -- simple message
* _m($ctx, $msg) -- message with context
* _m($msg1, $msg2, $n) -- message that can be singular or plural
* _m($ctx, $msg1, $msg2, $n) -- combination of the previous two
*
* @param string $msg
* @param extra params as described above
*
* @throws InvalidArgumentException
*
* @return string
*
* @todo add parameters
*/
function _m(string $msg /*, ...*/): string
{
$domain = I18nHelper::_mdomain(debug_backtrace()[0]['file']);
$args = func_get_args();
switch (count($args)) {
case 1:
// Empty parameters
return I18nHelper::$translator->trans($msg, [], $domain);
case 2:
$context = $args[0];
$msg_single = $args[1];
// ASCII 4 is EOT, used to separate context from string
return I18nHelper::$translator->trans($context . '\004' . $msg_single, [], $domain);
case 3:
// '|' separates the singular from the plural version
$msg_single = $args[0];
$msg_plural = $args[1];
$n = $args[2];
return I18nHelper::$translator->trans($msg_single . '|' . $msg_plural, ['%d' => $n], $domain);
case 4:
// Combine both
$context = $args[0];
$msg_single = $args[1];
$msg_plural = $args[2];
$n = $args[3];
return I18nHelper::$translator->trans($context . '\004' . $msg_single . '|' . $msg_plural,
['%d' => $n], $domain);
default:
throw new InvalidArgumentException('Bad parameter count to _m()');
}
}

View File

@ -18,44 +18,25 @@
// }}} // }}}
/** /**
* Utility functions for i18n * Dynamic router loader and URLMapper interface atop Symfony's router
* *
* @category I18n * Converts a path into a set of parameters, and vice versa
* @package GNU social *
* @package GNUsocial
* @category URL
* *
* @author Matthew Gregg <matthew.gregg@gmail.com>
* @author Ciaran Gultnieks <ciaran@ciarang.com>
* @author Evan Prodromou <evan@status.net>
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Hugo Sales <hugo@fc.up.pt> * @author Hugo Sales <hugo@fc.up.pt>
* @copyright 2010, 2018-2020 Free Software Foundation, Inc http://www.fsf.org * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @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
*/ */
namespace App\Core; namespace App\Core\I18n;
// Locale category constants are usually predefined, but may not be
// on some systems such as Win32.
$LC_CATEGORIES = [
'LC_CTYPE',
'LC_NUMERIC',
'LC_TIME',
'LC_COLLATE',
'LC_MONETARY',
'LC_MESSAGES',
'LC_ALL',
];
foreach ($LC_CATEGORIES as $key => $name) {
if (!defined($name)) {
define($name, $key);
}
}
use App\Util\Formatting; use App\Util\Formatting;
use InvalidArgumentException; use InvalidArgumentException;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
abstract class I18n abstract class I18nHelper
{ {
public static ?TranslatorInterface $translator = null; public static ?TranslatorInterface $translator = null;
@ -65,15 +46,12 @@ abstract class I18n
} }
/** /**
* Looks for which plugin we've been called from to set the gettext domain; * Looks for which plugin we've been called from to get the gettext domain;
* if not in a plugin subdirectory, we'll use the default 'gnusocial'. * if not in a plugin subdirectory, we'll use the default 'Core'.
*
* @param array $backtrace debug_backtrace() output
* *
* @return string * @return string
* @private
*/ */
public static function _mdomain(array $backtrace): string public static function _mdomain(string $path): string
{ {
/* /*
0 => 0 =>
@ -86,7 +64,6 @@ abstract class I18n
0 => &string 'Feeds' (length=5) 0 => &string 'Feeds' (length=5)
*/ */
static $cached; static $cached;
$path = $backtrace[0]['file'];
if (!isset($cached[$path])) { if (!isset($cached[$path])) {
$path = Formatting::normalizePath($path); $path = Formatting::normalizePath($path);
$cached[$path] = Formatting::pluginFromPath($path); $cached[$path] = Formatting::pluginFromPath($path);
@ -237,59 +214,4 @@ abstract class I18n
'vi' => ['q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'], 'vi' => ['q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'],
]; ];
} }
/**
* Wrapper for symfony translation with smart domain detection.
*
* If calling from a plugin, this function checks which plugin it was
* being called from and uses that as text domain, which will have
* been set up during plugin initialization.
*
* Also handles plurals and contexts depending on what parameters
* are passed to it:
*
* _m($msg) -- simple message
* _m($ctx, $msg) -- message with context
* _m($msg1, $msg2, $n) -- message that can be singular or plural
* _m($ctx, $msg1, $msg2, $n) -- combination of the previous two
*
* @param string $msg
* @param extra params as described above
*
* @throws InvalidArgumentException
*
* @return string
*
*/
public static function _m(string $msg /*, ...*/): string
{
$domain = self::_mdomain(debug_backtrace());
$args = func_get_args();
switch (count($args)) {
case 1:
// Empty parameters
return self::$translator->trans($msg, [], $domain);
case 2:
$context = $args[0];
$msg_single = $args[1];
// ASCII 4 is EOT, used to separate context from string
return self::$translator->trans($context . '\004' . $msg_single, [], $domain);
case 3:
// '|' separates the singular from the plural version
$msg_single = $args[0];
$msg_plural = $args[1];
$n = $args[2];
return self::$translator->trans($msg_single . '|' . $msg_plural, ['%d' => $n], $domain);
case 4:
// Combine both
$context = $args[0];
$msg_single = $args[1];
$msg_plural = $args[2];
$n = $args[3];
return self::$translator->trans($context . '\004' . $msg_single . '|' . $msg_plural,
['%d' => $n], $domain);
default:
throw new InvalidArgumentException('Bad parameter count to _m()');
}
}
} }