1: <?php
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: App::import('Core', 'l10n');
30: 31: 32: 33: 34: 35: 36: 37:
38: class I18n extends Object {
39: 40: 41: 42: 43: 44:
45: var $l10n = null;
46: 47: 48: 49: 50: 51:
52: var $domain = null;
53: 54: 55: 56: 57: 58:
59: var $category = 'LC_MESSAGES';
60: 61: 62: 63: 64: 65:
66: var $__lang = null;
67: 68: 69: 70: 71: 72:
73: var $__domains = array();
74: 75: 76: 77: 78: 79: 80:
81: var $__noLocale = false;
82: 83: 84: 85: 86: 87:
88: var $__cache = array();
89: 90: 91: 92: 93: 94: 95:
96: var $__categories = array(
97: 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
98: );
99: 100: 101: 102: 103: 104:
105: function &getInstance() {
106: static $instance = array();
107: if (!$instance) {
108: $instance[0] =& new I18n();
109: $instance[0]->l10n =& new L10n();
110: }
111: return $instance[0];
112: }
113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124:
125: function translate($singular, $plural = null, $domain = null, $category = 6, $count = null) {
126: $_this =& I18n::getInstance();
127:
128: if (strpos($singular, "\r\n") !== false) {
129: $singular = str_replace("\r\n", "\n", $singular);
130: }
131: if ($plural !== null && strpos($plural, "\r\n") !== false) {
132: $plural = str_replace("\r\n", "\n", $plural);
133: }
134:
135: if (is_numeric($category)) {
136: $_this->category = $_this->__categories[$category];
137: }
138: $language = Configure::read('Config.language');
139:
140: if (!empty($_SESSION['Config']['language'])) {
141: $language = $_SESSION['Config']['language'];
142: }
143:
144: if (($_this->__lang && $_this->__lang !== $language) || !$_this->__lang) {
145: $lang = $_this->l10n->get($language);
146: $_this->__lang = $lang;
147: }
148:
149: if (is_null($domain)) {
150: $domain = 'default';
151: }
152: $_this->domain = $domain . '_' . $_this->l10n->locale;
153:
154: if (!isset($_this->__domains[$_this->category][$_this->__lang][$domain])) {
155: $_this->__domains[$_this->category][$_this->__lang][$domain] = Cache::read($_this->domain, '_cake_core_');
156: }
157:
158: if (empty($_this->__domains[$_this->category][$_this->__lang][$domain])) {
159: $_this->__bindTextDomain($domain);
160: $_this->__cache[] = array(
161: 'key' => $_this->domain,
162: 'category' => $_this->category,
163: 'lang' => $_this->__lang,
164: 'domain' => $domain,
165: 'locale' => $_this->l10n->locale
166: );
167: }
168:
169: if (!isset($count)) {
170: $plurals = 0;
171: } elseif (!empty($_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"]) && $_this->__noLocale === false) {
172: $header = $_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"];
173: $plurals = $_this->__pluralGuess($header, $count);
174: } else {
175: if ($count != 1) {
176: $plurals = 1;
177: } else {
178: $plurals = 0;
179: }
180: }
181:
182: if (!empty($_this->__domains[$_this->category][$_this->__lang][$domain][$singular])) {
183: if (($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$singular]) || ($plurals) && ($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$plural])) {
184: if (is_array($trans)) {
185: if (isset($trans[$plurals])) {
186: $trans = $trans[$plurals];
187: }
188: }
189: if (strlen($trans)) {
190: $singular = $trans;
191: return $singular;
192: }
193: }
194: }
195:
196: if (!empty($plurals)) {
197: return($plural);
198: }
199: return($singular);
200: }
201: 202: 203: 204: 205: 206: 207: 208:
209: function __pluralGuess($header, $n) {
210: if (!is_string($header) || $header === "nplurals=1;plural=0;" || !isset($header[0])) {
211: return 0;
212: }
213:
214: if ($header === "nplurals=2;plural=n!=1;") {
215: return $n != 1 ? 1 : 0;
216: } elseif ($header === "nplurals=2;plural=n>1;") {
217: return $n > 1 ? 1 : 0;
218: }
219:
220: if (strpos($header, "plurals=3")) {
221: if (strpos($header, "100!=11")) {
222: if (strpos($header, "10<=4")) {
223: return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
224: } elseif (strpos($header, "100<10")) {
225: return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
226: }
227: return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n != 0 ? 1 : 2);
228: } elseif (strpos($header, "n==2")) {
229: return $n == 1 ? 0 : ($n == 2 ? 1 : 2);
230: } elseif (strpos($header, "n==0")) {
231: return $n == 1 ? 0 : ($n == 0 || ($n % 100 > 0 && $n % 100 < 20) ? 1 : 2);
232: } elseif (strpos($header, "n>=2")) {
233: return $n == 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2);
234: } elseif (strpos($header, "10>=2")) {
235: return $n == 1 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
236: }
237: return $n % 10 == 1 ? 0 : ($n % 10 == 2 ? 1 : 2);
238: } elseif (strpos($header, "plurals=4")) {
239: if (strpos($header, "100==2")) {
240: return $n % 100 == 1 ? 0 : ($n % 100 == 2 ? 1 : ($n % 100 == 3 || $n % 100 == 4 ? 2 : 3));
241: } elseif (strpos($header, "n>=3")) {
242: return $n == 1 ? 0 : ($n == 2 ? 1 : ($n == 0 || ($n >= 3 && $n <= 10) ? 2 : 3));
243: } elseif (strpos($header, "100>=1")) {
244: return $n == 1 ? 0 : ($n == 0 || ($n % 100 >= 1 && $n % 100 <= 10) ? 1 : ($n % 100 >= 11 && $n % 100 <= 20 ? 2 : 3));
245: }
246: } elseif (strpos($header, "plurals=5")) {
247: return $n == 1 ? 0 : ($n == 2 ? 1 : ($n >= 3 && $n <= 6 ? 2 : ($n >= 7 && $n <= 10 ? 3 : 4)));
248: }
249: }
250: 251: 252: 253: 254: 255: 256:
257: function __bindTextDomain($domain) {
258: $this->__noLocale = true;
259: $core = true;
260: $merge = array();
261: $searchPaths = Configure::read('localePaths');
262: $plugins = Configure::listObjects('plugin');
263:
264: if (!empty($plugins)) {
265: $pluginPaths = Configure::read('pluginPaths');
266:
267: foreach ($plugins as $plugin) {
268: $plugin = Inflector::underscore($plugin);
269: if ($plugin === $domain) {
270: foreach ($pluginPaths as $pluginPath) {
271: $searchPaths[] = $pluginPath . $plugin . DS . 'locale' . DS;
272: }
273: $searchPaths = array_reverse($searchPaths);
274: break;
275: }
276: }
277: }
278:
279:
280: foreach ($searchPaths as $directory) {
281:
282: foreach ($this->l10n->languagePath as $lang) {
283: $file = $directory . $lang . DS . $this->category . DS . $domain;
284:
285: if ($core) {
286: $app = $directory . $lang . DS . $this->category . DS . 'core';
287:
288: if (file_exists($fn = "$app.mo")) {
289: $this->__loadMo($fn, $domain);
290: $this->__noLocale = false;
291: $merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain];
292: $core = null;
293: } elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) {
294: $this->__loadPo($f, $domain);
295: $this->__noLocale = false;
296: $merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain];
297: $core = null;
298: }
299: }
300:
301: if (file_exists($fn = "$file.mo")) {
302: $this->__loadMo($fn, $domain);
303: $this->__noLocale = false;
304: break 2;
305: } elseif (file_exists($fn = "$file.po") && ($f = fopen($fn, "r"))) {
306: $this->__loadPo($f, $domain);
307: $this->__noLocale = false;
308: break 2;
309: }
310: }
311: }
312:
313: if (empty($this->__domains[$this->category][$this->__lang][$domain])) {
314: $this->__domains[$this->category][$this->__lang][$domain] = array();
315: return($domain);
316: }
317:
318: if ($head = $this->__domains[$this->category][$this->__lang][$domain][""]) {
319: foreach (explode("\n", $head) as $line) {
320: $header = strtok($line,":");
321: $line = trim(strtok("\n"));
322: $this->__domains[$this->category][$this->__lang][$domain]["%po-header"][strtolower($header)] = $line;
323: }
324:
325: if (isset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"])) {
326: $switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"]);
327: $this->__domains[$this->category][$this->__lang][$domain]["%plural-c"] = $switch;
328: unset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]);
329: }
330: $this->__domains = Set::pushDiff($this->__domains, $merge);
331:
332: if (isset($this->__domains[$this->category][$this->__lang][$domain][null])) {
333: unset($this->__domains[$this->category][$this->__lang][$domain][null]);
334: }
335: }
336: return($domain);
337: }
338: 339: 340: 341: 342: 343: 344:
345: function __loadMo($file, $domain) {
346: $data = file_get_contents($file);
347:
348: if ($data) {
349: $header = substr($data, 0, 20);
350: $header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header);
351: extract($header);
352:
353: if ((dechex($magic) == '950412de' || dechex($magic) == 'ffffffff950412de') && $version == 0) {
354: for ($n = 0; $n < $count; $n++) {
355: $r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8));
356: $msgid = substr($data, $r["offs"], $r["len"]);
357: unset($msgid_plural);
358:
359: if (strpos($msgid, "\000")) {
360: list($msgid, $msgid_plural) = explode("\000", $msgid);
361: }
362: $r = unpack("L1len/L1offs", substr($data, $o_trn + $n * 8, 8));
363: $msgstr = substr($data, $r["offs"], $r["len"]);
364:
365: if (strpos($msgstr, "\000")) {
366: $msgstr = explode("\000", $msgstr);
367: }
368: $this->__domains[$this->category][$this->__lang][$domain][$msgid] = $msgstr;
369:
370: if (isset($msgid_plural)) {
371: $this->__domains[$this->category][$this->__lang][$domain][$msgid_plural] =& $this->__domains[$this->category][$this->__lang][$domain][$msgid];
372: }
373: }
374: }
375: }
376: }
377: 378: 379: 380: 381: 382: 383: 384:
385: function __loadPo($file, $domain) {
386: $type = 0;
387: $translations = array();
388: $translationKey = "";
389: $plural = 0;
390: $header = "";
391:
392: do {
393: $line = trim(fgets($file, 1024));
394: if ($line == "" || $line[0] == "#") {
395: continue;
396: }
397: if (preg_match("/msgid[[:space:]]+\"(.+)\"$/i", $line, $regs)) {
398: $type = 1;
399: $translationKey = stripcslashes($regs[1]);
400: } elseif (preg_match("/msgid[[:space:]]+\"\"$/i", $line, $regs)) {
401: $type = 2;
402: $translationKey = "";
403: } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && ($type == 1 || $type == 2 || $type == 3)) {
404: $type = 3;
405: $translationKey .= stripcslashes($regs[1]);
406: } elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
407: $translations[$translationKey] = stripcslashes($regs[1]);
408: $type = 4;
409: } elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
410: $type = 4;
411: $translations[$translationKey] = "";
412: } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 4 && $translationKey) {
413: $translations[$translationKey] .= stripcslashes($regs[1]);
414: } elseif (preg_match("/msgid_plural[[:space:]]+\".*\"$/i", $line, $regs)) {
415: $type = 6;
416: } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 6 && $translationKey) {
417: $type = 6;
418: } elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
419: $plural = $regs[1];
420: $translations[$translationKey][$plural] = stripcslashes($regs[2]);
421: $type = 7;
422: } elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
423: $plural = $regs[1];
424: $translations[$translationKey][$plural] = "";
425: $type = 7;
426: } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 7 && $translationKey) {
427: $translations[$translationKey][$plural] .= stripcslashes($regs[1]);
428: } elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && $type == 2 && !$translationKey) {
429: $header .= stripcslashes($regs[1]);
430: $type = 5;
431: } elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && !$translationKey) {
432: $header = "";
433: $type = 5;
434: } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 5) {
435: $header .= stripcslashes($regs[1]);
436: } else {
437: unset($translations[$translationKey]);
438: $type = 0;
439: $translationKey = "";
440: $plural = 0;
441: }
442: } while (!feof($file));
443: fclose($file);
444: $merge[""] = $header;
445: return $this->__domains[$this->category][$this->__lang][$domain] = array_merge($merge ,$translations);
446: }
447: 448: 449: 450: 451: 452:
453: function __destruct() {
454: if (!empty($this->__cache)) {
455: foreach($this->__cache as $entry) {
456: if (empty($this->__domains[$entry['category']][$entry['lang']][$entry['domain']])) {
457: continue;
458: }
459: Cache::write(
460: $entry['key'],
461: array_filter($this->__domains[$entry['category']][$entry['lang']][$entry['domain']]),
462: '_cake_core_'
463: );
464: }
465: }
466: $this->__cache = array();
467: $this->__domains = array();
468: }
469: }
470: ?>
471: