]> git.immae.eu Git - github/shaarli/Shaarli.git/blobdiff - application/Languages.php
Shaarli's translation
[github/shaarli/Shaarli.git] / application / Languages.php
index c8b0a25a355c2a60ffbaf33a16cb6d3d06ee1b5a..4ba32f29384d55490af8db47d5416de615216f80 100644 (file)
 <?php
 
+namespace Shaarli;
+
+use Gettext\GettextTranslator;
+use Gettext\Merge;
+use Gettext\Translations;
+use Gettext\Translator;
+use Gettext\TranslatorInterface;
+use Shaarli\Config\ConfigManager;
+
 /**
- * Wrapper function for translation which match the API
- * of gettext()/_() and ngettext().
+ * Class Languages
+ *
+ * Load Shaarli translations using 'gettext/gettext'.
+ * This class allows to either use PHP gettext extension, or a PHP implementation of gettext,
+ * with a fixed language, or dynamically using autoLocale().
  *
- * Not doing translation for now.
+ * Translation files PO/MO files follow gettext standard and must be placed under:
+ *   <translation path>/<language>/LC_MESSAGES/<domain>.[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);
 }