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