]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | ||
3 | namespace Shaarli; | |
4 | ||
5 | use Gettext\GettextTranslator; | |
6 | use Gettext\Merge; | |
7 | use Gettext\Translations; | |
8 | use Gettext\Translator; | |
9 | use Gettext\TranslatorInterface; | |
10 | use Shaarli\Config\ConfigManager; | |
11 | ||
12 | /** | |
13 | * Class Languages | |
14 | * | |
15 | * Load Shaarli translations using 'gettext/gettext'. | |
16 | * This class allows to either use PHP gettext extension, or a PHP implementation of gettext, | |
17 | * with a fixed language, or dynamically using autoLocale(). | |
18 | * | |
19 | * Translation files PO/MO files follow gettext standard and must be placed under: | |
20 | * <translation path>/<language>/LC_MESSAGES/<domain>.[po|mo] | |
21 | * | |
22 | * Pros/cons: | |
23 | * - gettext extension is faster | |
24 | * - gettext is very system dependent (PHP extension, the locale must be installed, and web server reloaded) | |
25 | * | |
26 | * Settings: | |
27 | * - translation.mode: | |
28 | * - auto: use default setting (PHP implementation) | |
29 | * - php: use PHP implementation | |
30 | * - gettext: use gettext wrapper | |
31 | * - translation.language: | |
32 | * - auto: use autoLocale() and the language change according to user HTTP headers | |
33 | * - fixed language: e.g. 'fr' | |
34 | * - translation.extensions: | |
35 | * - domain => translation_path: allow plugins and themes to extend the defaut extension | |
36 | * The domain must be unique, and translation path must be relative, and contains the tree mentioned above. | |
37 | * | |
38 | * @package Shaarli | |
39 | */ | |
40 | class Languages | |
41 | { | |
42 | /** | |
43 | * Core translations domain | |
44 | */ | |
45 | const DEFAULT_DOMAIN = 'shaarli'; | |
46 | ||
47 | /** | |
48 | * @var TranslatorInterface | |
49 | */ | |
50 | protected $translator; | |
51 | ||
52 | /** | |
53 | * @var string | |
54 | */ | |
55 | protected $language; | |
56 | ||
57 | /** | |
58 | * @var ConfigManager | |
59 | */ | |
60 | protected $conf; | |
61 | ||
62 | /** | |
63 | * Languages constructor. | |
64 | * | |
65 | * @param string $language lang determined by autoLocale(), can be overridden. | |
66 | * @param ConfigManager $conf instance. | |
67 | */ | |
68 | public function __construct($language, $conf) | |
69 | { | |
70 | $this->conf = $conf; | |
71 | $confLanguage = $this->conf->get('translation.language', 'auto'); | |
72 | if ($confLanguage === 'auto' || ! $this->isValidLanguage($confLanguage)) { | |
73 | $this->language = substr($language, 0, 5); | |
74 | } else { | |
75 | $this->language = $confLanguage; | |
76 | } | |
77 | ||
78 | if (! extension_loaded('gettext') | |
79 | || in_array($this->conf->get('translation.mode', 'auto'), ['auto', 'php']) | |
80 | ) { | |
81 | $this->initPhpTranslator(); | |
82 | } else { | |
83 | $this->initGettextTranslator(); | |
84 | } | |
85 | ||
86 | // Register default functions (e.g. '__()') to use our Translator | |
87 | $this->translator->register(); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Initialize the translator using php gettext extension (gettext dependency act as a wrapper). | |
92 | */ | |
93 | protected function initGettextTranslator () | |
94 | { | |
95 | $this->translator = new GettextTranslator(); | |
96 | $this->translator->setLanguage($this->language); | |
97 | $this->translator->loadDomain(self::DEFAULT_DOMAIN, 'inc/languages'); | |
98 | ||
99 | foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { | |
100 | if ($domain !== self::DEFAULT_DOMAIN) { | |
101 | $this->translator->loadDomain($domain, $translationPath, false); | |
102 | } | |
103 | } | |
104 | } | |
105 | ||
106 | /** | |
107 | * Initialize the translator using a PHP implementation of gettext. | |
108 | * | |
109 | * Note that if language po file doesn't exist, errors are ignored (e.g. not installed language). | |
110 | */ | |
111 | protected function initPhpTranslator() | |
112 | { | |
113 | $this->translator = new Translator(); | |
114 | $translations = new Translations(); | |
115 | // Core translations | |
116 | try { | |
117 | /** @var Translations $translations */ | |
118 | $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po'); | |
119 | $translations->setDomain('shaarli'); | |
120 | $this->translator->loadTranslations($translations); | |
121 | } catch (\InvalidArgumentException $e) {} | |
122 | ||
123 | ||
124 | // Extension translations (plugins, themes, etc.). | |
125 | foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { | |
126 | if ($domain === self::DEFAULT_DOMAIN) { | |
127 | continue; | |
128 | } | |
129 | ||
130 | try { | |
131 | /** @var Translations $extension */ | |
132 | $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'); | |
133 | $extension->setDomain($domain); | |
134 | $this->translator->loadTranslations($extension); | |
135 | } catch (\InvalidArgumentException $e) {} | |
136 | } | |
137 | } | |
138 | ||
139 | /** | |
140 | * Checks if a language string is valid. | |
141 | * | |
142 | * @param string $language e.g. 'fr' or 'en_US' | |
143 | * | |
144 | * @return bool true if valid, false otherwise | |
145 | */ | |
146 | protected function isValidLanguage($language) | |
147 | { | |
148 | return preg_match('/^[a-z]{2}(_[A-Z]{2})?/', $language) === 1; | |
149 | } | |
150 | ||
151 | /** | |
152 | * Get the list of available languages for Shaarli. | |
153 | * | |
154 | * @return array List of available languages, with their label. | |
155 | */ | |
156 | public static function getAvailableLanguages() | |
157 | { | |
158 | return [ | |
159 | 'auto' => t('Automatic'), | |
160 | 'en' => t('English'), | |
161 | 'fr' => t('French'), | |
162 | ]; | |
163 | } | |
164 | } |