From 12266213d098a53c5f005b9afcbbe62771fd580c Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 9 May 2017 18:12:15 +0200 Subject: Shaarli's translation * translation system and unit tests * Translations everywhere Dont use translation merge It is not available with PHP builtin gettext, so it would have lead to inconsistency. --- application/Languages.php | 153 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 12 deletions(-) (limited to 'application/Languages.php') diff --git a/application/Languages.php b/application/Languages.php index c8b0a25a..4ba32f29 100644 --- a/application/Languages.php +++ b/application/Languages.php @@ -1,21 +1,150 @@ //LC_MESSAGES/.[po|mo] * - * @param string $text Text to translate. - * @param string $nText The plural message ID. - * @param int $nb The number of items for plural forms. + * Pros/cons: + * - gettext extension is faster + * - gettext is very system dependent (PHP extension, the locale must be installed, and web server reloaded) * - * @return String Text translated. + * Settings: + * - translation.mode: + * - auto: use default setting (PHP implementation) + * - php: use PHP implementation + * - gettext: use gettext wrapper + * - translation.language: + * - auto: use autoLocale() and the language change according to user HTTP headers + * - fixed language: e.g. 'fr' + * - translation.extensions: + * - domain => translation_path: allow plugins and themes to extend the defaut extension + * The domain must be unique, and translation path must be relative, and contains the tree mentioned above. + * + * @package Shaarli */ -function t($text, $nText = '', $nb = 0) { - if (empty($nText)) { - return $text; +class Languages +{ + /** + * Core translations domain + */ + const DEFAULT_DOMAIN = 'shaarli'; + + /** + * @var TranslatorInterface + */ + protected $translator; + + /** + * @var string + */ + protected $language; + + /** + * @var ConfigManager + */ + protected $conf; + + /** + * Languages constructor. + * + * @param string $language lang determined by autoLocale(), can be override. + * @param ConfigManager $conf instance. + */ + public function __construct($language, $conf) + { + $this->conf = $conf; + $confLanguage = $this->conf->get('translation.language', 'auto'); + if ($confLanguage === 'auto' || ! $this->isValidLanguage($confLanguage)) { + $this->language = substr($language, 0, 5); + } else { + $this->language = $confLanguage; + } + + if (! extension_loaded('gettext') + || in_array($this->conf->get('translation.mode', 'auto'), ['auto', 'php']) + ) { + $this->initPhpTranslator(); + } else { + $this->initGettextTranslator(); + } + + // Register default functions (e.g. '__()') to use our Translator + $this->translator->register(); + } + + /** + * Initialize the translator using php gettext extension (gettext dependency act as a wrapper). + */ + protected function initGettextTranslator () + { + $this->translator = new GettextTranslator(); + $this->translator->setLanguage($this->language); + $this->translator->loadDomain(self::DEFAULT_DOMAIN, 'inc/languages'); + + foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { + if ($domain !== self::DEFAULT_DOMAIN) { + $this->translator->loadDomain($domain, $translationPath, false); + } + } + } + + /** + * Initialize the translator using a PHP implementation of gettext. + * + * Note that if language po file doesn't exist, errors are ignored (e.g. not installed language). + */ + protected function initPhpTranslator() + { + $this->translator = new Translator(); + $translations = new Translations(); + // Core translations + try { + /** @var Translations $translations */ + $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po'); + $translations->setDomain('shaarli'); + $this->translator->loadTranslations($translations); + } catch (\InvalidArgumentException $e) {} + + + // Extension translations (plugins, themes, etc.). + foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { + if ($domain === self::DEFAULT_DOMAIN) { + continue; + } + + try { + /** @var Translations $extension */ + $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'); + $extension->setDomain($domain); + $this->translator->loadTranslations($extension); + } catch (\InvalidArgumentException $e) {} + } + } + + /** + * Checks if a language string is valid. + * + * @param string $language e.g. 'fr' or 'en_US' + * + * @return bool true if valid, false otherwise + */ + protected function isValidLanguage($language) + { + return preg_match('/^[a-z]{2}(_[A-Z]{2})?/', $language) === 1; } - $actualForm = $nb > 1 ? $nText : $text; - return sprintf($actualForm, $nb); } -- cgit v1.2.3 From f39580c6fd171b849cec5832b4912182696341f2 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 25 May 2017 13:26:05 +0200 Subject: Add language selection in the configure page of the default theme --- application/Languages.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'application/Languages.php') diff --git a/application/Languages.php b/application/Languages.php index 4ba32f29..357c7524 100644 --- a/application/Languages.php +++ b/application/Languages.php @@ -62,7 +62,7 @@ class Languages /** * Languages constructor. * - * @param string $language lang determined by autoLocale(), can be override. + * @param string $language lang determined by autoLocale(), can be overridden. * @param ConfigManager $conf instance. */ public function __construct($language, $conf) @@ -147,4 +147,18 @@ class Languages { return preg_match('/^[a-z]{2}(_[A-Z]{2})?/', $language) === 1; } + + /** + * Get the list of available languages for Shaarli. + * + * @return array List of available languages, with their label. + */ + public static function getAvailableLanguages() + { + return [ + 'auto' => t('Automatic'), + 'en' => t('English'), + 'fr' => t('French'), + ]; + } } -- cgit v1.2.3