]>
Commit | Line | Data |
---|---|---|
edf3ff5a A |
1 | <?php |
2 | ||
12266213 A |
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 | ||
edf3ff5a | 12 | /** |
12266213 A |
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(). | |
edf3ff5a | 18 | * |
12266213 A |
19 | * Translation files PO/MO files follow gettext standard and must be placed under: |
20 | * <translation path>/<language>/LC_MESSAGES/<domain>.[po|mo] | |
edf3ff5a | 21 | * |
12266213 A |
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) | |
edf3ff5a | 25 | * |
12266213 A |
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 | |
edf3ff5a | 39 | */ |
12266213 A |
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 | * | |
f39580c6 | 65 | * @param string $language lang determined by autoLocale(), can be overridden. |
12266213 A |
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'); | |
b7c412d4 A |
72 | // Auto mode or invalid parameter, use the detected language. |
73 | // If the detected language is invalid, it doesn't matter, it will use English. | |
12266213 A |
74 | if ($confLanguage === 'auto' || ! $this->isValidLanguage($confLanguage)) { |
75 | $this->language = substr($language, 0, 5); | |
76 | } else { | |
77 | $this->language = $confLanguage; | |
78 | } | |
79 | ||
80 | if (! extension_loaded('gettext') | |
81 | || in_array($this->conf->get('translation.mode', 'auto'), ['auto', 'php']) | |
82 | ) { | |
83 | $this->initPhpTranslator(); | |
84 | } else { | |
85 | $this->initGettextTranslator(); | |
86 | } | |
87 | ||
88 | // Register default functions (e.g. '__()') to use our Translator | |
89 | $this->translator->register(); | |
90 | } | |
91 | ||
92 | /** | |
93 | * Initialize the translator using php gettext extension (gettext dependency act as a wrapper). | |
94 | */ | |
95 | protected function initGettextTranslator () | |
96 | { | |
97 | $this->translator = new GettextTranslator(); | |
98 | $this->translator->setLanguage($this->language); | |
99 | $this->translator->loadDomain(self::DEFAULT_DOMAIN, 'inc/languages'); | |
100 | ||
68c6afc5 A |
101 | // Default extension translation from the current theme |
102 | $themeTransFolder = rtrim($this->conf->get('raintpl_tpl'), '/') .'/'. $this->conf->get('theme') .'/language'; | |
103 | if (is_dir($themeTransFolder)) { | |
104 | $this->translator->loadDomain($this->conf->get('theme'), $themeTransFolder, false); | |
105 | } | |
106 | ||
12266213 A |
107 | foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { |
108 | if ($domain !== self::DEFAULT_DOMAIN) { | |
109 | $this->translator->loadDomain($domain, $translationPath, false); | |
110 | } | |
111 | } | |
112 | } | |
113 | ||
114 | /** | |
115 | * Initialize the translator using a PHP implementation of gettext. | |
116 | * | |
117 | * Note that if language po file doesn't exist, errors are ignored (e.g. not installed language). | |
118 | */ | |
119 | protected function initPhpTranslator() | |
120 | { | |
121 | $this->translator = new Translator(); | |
122 | $translations = new Translations(); | |
123 | // Core translations | |
124 | try { | |
12266213 A |
125 | $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po'); |
126 | $translations->setDomain('shaarli'); | |
127 | $this->translator->loadTranslations($translations); | |
128 | } catch (\InvalidArgumentException $e) {} | |
129 | ||
68c6afc5 A |
130 | // Default extension translation from the current theme |
131 | $theme = $this->conf->get('theme'); | |
132 | $themeTransFolder = rtrim($this->conf->get('raintpl_tpl'), '/') .'/'. $theme .'/language'; | |
133 | if (is_dir($themeTransFolder)) { | |
134 | try { | |
135 | $translations = Translations::fromPoFile( | |
136 | $themeTransFolder .'/'. $this->language .'/LC_MESSAGES/'. $theme .'.po' | |
137 | ); | |
138 | $translations->setDomain($theme); | |
139 | $this->translator->loadTranslations($translations); | |
140 | } catch (\InvalidArgumentException $e) {} | |
141 | } | |
12266213 A |
142 | |
143 | // Extension translations (plugins, themes, etc.). | |
144 | foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { | |
145 | if ($domain === self::DEFAULT_DOMAIN) { | |
146 | continue; | |
147 | } | |
148 | ||
149 | try { | |
12266213 A |
150 | $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'); |
151 | $extension->setDomain($domain); | |
152 | $this->translator->loadTranslations($extension); | |
153 | } catch (\InvalidArgumentException $e) {} | |
154 | } | |
155 | } | |
156 | ||
157 | /** | |
158 | * Checks if a language string is valid. | |
159 | * | |
160 | * @param string $language e.g. 'fr' or 'en_US' | |
161 | * | |
162 | * @return bool true if valid, false otherwise | |
163 | */ | |
164 | protected function isValidLanguage($language) | |
165 | { | |
166 | return preg_match('/^[a-z]{2}(_[A-Z]{2})?/', $language) === 1; | |
edf3ff5a | 167 | } |
f39580c6 A |
168 | |
169 | /** | |
170 | * Get the list of available languages for Shaarli. | |
171 | * | |
172 | * @return array List of available languages, with their label. | |
173 | */ | |
174 | public static function getAvailableLanguages() | |
175 | { | |
176 | return [ | |
177 | 'auto' => t('Automatic'), | |
178 | 'en' => t('English'), | |
179 | 'fr' => t('French'), | |
7ca12407 | 180 | 'de' => t('German'), |
f39580c6 A |
181 | ]; |
182 | } | |
edf3ff5a | 183 | } |