aboutsummaryrefslogtreecommitdiff
path: root/lib/Minz/Translate.php
blob: baddcb42480b59b4620a818d4aa4e8a12aa789b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
<?php
/** 
 * MINZ - Copyright 2011 Marien Fressinaud
 * Sous licence AGPL3 <http://www.gnu.org/licenses/>
 */

/**
 * This class is used for the internationalization.
 * It uses files in `./app/i18n/`
 */
class Minz_Translate {
	/**
	 * $path_list is the list of registered base path to search translations.
	 */
	private static $path_list = array();

	/**
	 * $lang_name is the name of the current language to use.
	 */
	private static $lang_name;

	/**
	 * $lang_files is a list of registered i18n files.
	 */
	private static $lang_files = array();

	/**
	 * $translates is a cache for i18n translation.
	 */
	private static $translates = array();

	/**
	 * Init the translation object.
	 * @param $lang_name the lang to show.
	 */
	public static function init($lang_name = null) {
		self::$lang_name = $lang_name;
		self::$lang_files = array();
		self::$translates = array();
		self::registerPath(APP_PATH . '/i18n');
		foreach (self::$path_list as $path) {
			self::loadLang($path);
		}
	}

	/**
	 * Reset the translation object with a new language.
	 * @param $lang_name the new language to use
	 */
	public static function reset($lang_name) {
		self::$lang_name = $lang_name;
		self::$lang_files = array();
		self::$translates = array();
		foreach (self::$path_list as $path) {
			self::loadLang($path);
		}
	}

	/**
	 * Return the list of available languages.
	 * @return an array containing langs found in different registered paths.
	 */
	public static function availableLanguages() {
		$list_langs = array();

		foreach (self::$path_list as $path) {
			$path_langs = array_values(array_diff(
				scandir($path),
				array('..', '.')
			));

			$list_langs = array_merge($list_langs, $path_langs);
		}

		return array_unique($list_langs);
	}

	/**
	 * Register a new path.
	 * @param $path a path containing i18n directories (e.g. ./en/, ./fr/).
	 */
	public static function registerPath($path) {
		if (in_array($path, self::$path_list)) {
			return;
		}

		self::$path_list[] = $path;
		self::loadLang($path);
	}

	/**
	 * Load translations of the current language from the given path.
	 * @param $path the path containing i18n directories.
	 */
	private static function loadLang($path) {
		$lang_path = $path . '/' . self::$lang_name;
		if (!file_exists($lang_path) || is_null(self::$lang_name)) {
			// The lang path does not exist, nothing more to do.
			return;
		}

		$list_i18n_files = array_values(array_diff(
			scandir($lang_path),
			array('..', '.')
		));

		// Each file basename correspond to a top-level i18n key. For each of
		// these keys we store the file pathname and mark translations must be
		// reloaded (by setting $translates[$i18n_key] to null).
		foreach ($list_i18n_files as $i18n_filename) {
			$i18n_key = basename($i18n_filename, '.php');
			if (!isset(self::$lang_files[$i18n_key])) {
				self::$lang_files[$i18n_key] = array();
			}
			self::$lang_files[$i18n_key][] = $lang_path . '/' . $i18n_filename;
			self::$translates[$i18n_key] = null;
		}
	}

	/**
	 * Load the files associated to $key into $translates.
	 * @param $key the top level i18n key we want to load.
	 */
	private static function loadKey($key) {
		// The top level key is not in $lang_files, it means it does not exist!
		if (!isset(self::$lang_files[$key])) {
			Minz_Log::debug($key . ' is not a valid top level key');
			return false;
		}

		self::$translates[$key] = array();

		foreach (self::$lang_files[$key] as $lang_pathname) {
			$i18n_array = include($lang_pathname);
			if (!is_array($i18n_array)) {
				Minz_Log::warning('`' . $lang_pathname . '` does not contain a PHP array');
				continue;
			}

			// We must avoid to erase previous data so we just override them if
			// needed.
			self::$translates[$key] = array_replace_recursive(
				self::$translates[$key], $i18n_array
			);
		}

		return true;
	}

	/**
	 * Translate a key into its corresponding value based on selected language.
	 * @param $key the key to translate.
	 * @param additional parameters for variable keys.
	 * @return the value corresponding to the key.
	 *         If no value is found, return the key itself.
	 */ 
	public static function t($key) {
		$group = explode('.', $key);

		if (count($group) < 2) {
			Minz_Log::debug($key . ' is not in a valid format');
			$top_level = 'gen';
		} else {
			$top_level = array_shift($group);
		}

		// If $translates[$top_level] is null it means we have to load the
		// corresponding files.
		if (!isset(self::$translates[$top_level]) ||
				is_null(self::$translates[$top_level])) {
			$res = self::loadKey($top_level);
			if (!$res) {
				return $key;
			}
		}

		// Go through the i18n keys to get the correct translation value.
		$translates = self::$translates[$top_level];
		$size_group = count($group);
		$level_processed = 0;
		$translation_value = $key;
		foreach ($group as $i18n_level) {
			$level_processed++;
			if (!isset($translates[$i18n_level])) {
				Minz_Log::debug($key . ' is not a valid key');
				return $key;
			}

			if ($level_processed < $size_group) {
				$translates = $translates[$i18n_level];
			} else {
				$translation_value = $translates[$i18n_level];
			}
		}

		if (is_array($translation_value)) {
			if (isset($translation_value['_'])) {
				$translation_value = $translation_value['_'];
			} else {
				Minz_Log::debug($key . ' is not a valid key');
				return $key;
			}
		}

		// Get the facultative arguments to replace i18n variables.
		$args = func_get_args();
		unset($args[0]);

		return vsprintf($translation_value, $args);
	}

	/**
	 * Return the current language.
	 */
	public static function language() {
		return self::$lang_name;
	}
}


/**
 * Alias for Minz_Translate::t()
 */
function _t($key) {
	$args = func_get_args();
	unset($args[0]);
	array_unshift($args, $key);

	return call_user_func_array('Minz_Translate::t', $args);
}