LinksUpdate uses SELECT FOR UPDATE, thus starting a new transaction. So we have to...
[mediawiki.git] / PHPTAL-NP-0.7.0 / libs / GetText.php
blob029d51bd888b0aa8634c5ccadabf4ae10503339f
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 //
4 // Copyright (c) 2003 Laurent Bedubourg
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 //
20 // Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
21 //
23 require_once "PEAR.php";
25 define('GETTEXT_NATIVE', 1);
26 define('GETTEXT_PHP', 2);
28 /**
29 * Generic gettext static class.
31 * This class allows gettext usage with php even if the gettext support is
32 * not compiled in php.
34 * The developper can choose between the GETTEXT_NATIVE support and the
35 * GETTEXT_PHP support on initialisation. If native is not supported, the
36 * system will fall back to PHP support.
38 * On both systems, this package add a variable interpolation system so you can
39 * translate entire dynamic sentences in stead of peace of sentences.
41 * Small example without pear error lookup :
43 * <?php
44 * require_once "GetText.php";
46 * GetText::init();
47 * GetText::setLanguage('fr_Fr'); // may throw GetText_Error
48 * GetText::addDomain('myAppDomain'); // may throw GetText_Error
49 * GetText::setVar('login', $login);
50 * GetText::setVar('name', $name);
52 * // may throw GetText_Error
53 * echo GetText::gettext('Welcome ${name}, you\'re connected with login ${login}');
55 * // should echo something like :
56 * //
57 * // "Bienvenue Jean-Claude, vous ĂȘtes connectĂ© en tant qu'utilisateur jcaccount"
58 * //
59 * // or if fr_FR translation does not exists
60 * //
61 * // "Welcome Jean-Claude, you're connected with login jcaccount"
63 * ?>
65 * A gettext mini-howto should be provided with this package, if you're new
66 * to gettext usage, please read it to learn how to build a gettext
67 * translation directory (locale).
69 * @todo Tools to manage gettext files in php.
71 * - non traducted domains / keys
72 * - modification of keys
73 * - domain creation, preparation, delete, ...
74 * - tool to extract required messages from TOF templates
76 * @version 0.5
77 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
79 class GetText
81 /**
82 * This method returns current gettext support class.
84 * @return GetText_Support
85 * @static 1
86 * @access private
88 function &_support($set=false)
90 static $supportObject;
91 if ($set !== false) {
92 $supportObject = $set;
93 } elseif (!isset($supportObject)) {
94 trigger_error("GetText not initialized !". endl.
95 "Please call GetText::init() before calling ".
96 "any GetText function !".endl
97 , E_USER_ERROR);
99 return $supportObject;
103 * Initialize gettext package.
105 * This method instantiate the gettext support depending on managerType
106 * value.
108 * GETTEXT_NATIVE try to use gettext php support and fail back to PHP
109 * support if not installed.
111 * GETTEXT_PHP explicitely request the usage of PHP support.
113 * @param int $managerType
114 * Gettext support type.
116 * @access public
117 * @static 1
119 function init($managerType = GETTEXT_NATIVE)
121 if ($managerType == GETTEXT_NATIVE) {
122 if (function_exists('gettext')) {
123 return GetText::_support(new GetText_NativeSupport());
126 // fail back to php support
127 return GetText::_support(new GetText_PHPSupport());
131 * Set the language to use for traduction.
133 * @param string $langCode
134 * The language code usually defined as ll_CC, ll is the two letter
135 * language code and CC is the two letter country code.
137 * @throws GetText_Error if language is not supported by your system.
139 function setLanguage($langCode)
141 $support =& GetText::_support();
142 return $support->setLanguage($langCode);
146 * Add a translation domain.
148 * The domain name is usually the name of the .po file you wish to use.
149 * For example, if you created a file 'locale/ll_CC/LC_MESSAGES/myapp.po',
150 * you'll use 'myapp' as the domain name.
152 * @param string $domain
153 * The domain name.
155 * @param string $path optional
156 * The path to the locale directory (ie: /path/to/locale/) which
157 * contains ll_CC directories.
159 function addDomain($domain, $path=false)
161 $support =& GetText::_support();
162 return $support->addDomain($domain, $path);
166 * Retrieve the translation for specified key.
168 * @param string $key
169 * String to translate using gettext support.
171 function gettext($key)
173 $support =& GetText::_support();
174 return $support->gettext($key);
178 * Add a variable to gettext interpolation system.
180 * @param string $key
181 * The variable name.
183 * @param string $value
184 * The variable value.
186 function setVar($key, $value)
188 $support =& GetText::_support();
189 return $support->setVar($key, $value);
193 * Add an hashtable of variables.
195 * @param hashtable $hash
196 * PHP associative array of variables.
198 function setVars($hash)
200 $support =& GetText::_support();
201 return $support->setVars($hash);
205 * Reset interpolation variables.
207 function reset()
209 $support =& GetText::_support();
210 return $support->reset();
216 * Interface to gettext native support.
218 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
219 * @access private
221 class GetText_NativeSupport
223 var $_interpolationVars = array();
226 * Set gettext language code.
227 * @throws GetText_Error
229 function setLanguage($langCode)
231 putenv("LANG=$langCode");
232 putenv("LC_ALL=$langCode");
233 putenv("LANGUAGE=$langCode");
234 $set = setlocale(LC_ALL, "$langCode");
235 if ($set === false) {
236 $str = sprintf('Language code "%s" not supported by your system',
237 $langCode);
238 $err = new GetText_Error($str);
239 return PEAR::raiseError($err);
244 * Add a translation domain.
246 function addDomain($domain, $path=false)
248 if ($path === false) {
249 bindtextdomain($domain, "./locale/");
250 } else {
251 bindtextdomain($domain, $path);
253 textdomain($domain);
257 * Retrieve translation for specified key.
259 * @access private
261 function _getTranslation($key)
263 return gettext($key);
268 * Reset interpolation variables.
270 function reset()
272 $this->_interpolationVars = array();
276 * Set an interpolation variable.
278 function setVar($key, $value)
280 $this->_interpolationVars[$key] = $value;
284 * Set an associative array of interpolation variables.
286 function setVars($hash)
288 $this->_interpolationVars = array_merge($this->_interpolationVars,
289 $hash);
293 * Retrieve translation for specified key.
295 * @param string $key -- gettext msgid
296 * @throws GetText_Error
298 function gettext($key)
300 $value = $this->_getTranslation($key);
301 if ($value === false) {
302 $str = sprintf('Unable to locate gettext key "%s"', $key);
303 $err = new GetText_Error($str);
304 return PEAR::raiseError($err);
307 while (preg_match('/\$\{(.*?)\}/sm', $value, $m)) {
308 list($src, $var) = $m;
310 // retrieve variable to interpolate in context, throw an exception
311 // if not found.
312 $varValue = $this->_getVar($var);
313 if ($varValue === false) {
314 $str = sprintf('Interpolation error, var "%s" not set', $var);
315 $err = new GetText_Error($str);
316 return PEAR::raiseError($err);
318 $value = str_replace($src, $varValue, $value);
320 return $value;
324 * Retrieve an interpolation variable value.
326 * @return mixed
327 * @access private
329 function _getVar($name)
331 if (!array_key_exists($name, $this->_interpolationVars)) {
332 return false;
334 return $this->_interpolationVars[$name];
340 * Implementation of GetText support for PHP.
342 * This implementation is abble to cache .po files into php files returning the
343 * domain translation hashtable.
345 * @access private
346 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
348 class GetText_PHPSupport extends GetText_NativeSupport
350 var $_path = 'locale/';
351 var $_langCode = false;
352 var $_domains = array();
353 var $_end = -1;
354 var $_jobs = array();
357 * Set the translation domain.
359 * @param string $langCode -- language code
360 * @throws GetText_Error
362 function setLanguage($langCode)
364 // if language already set, try to reload domains
365 if ($this->_langCode !== false and $this->_langCode != $langCode) {
366 foreach ($this->_domains as $domain) {
367 $this->_jobs[] = array($domain->name, $domain->path);
369 $this->_domains = array();
370 $this->_end = -1;
373 $this->_langCode = $langCode;
375 // this allow us to set the language code after
376 // domain list.
377 while (count($this->_jobs) > 0) {
378 list($domain, $path) = array_shift($this->_jobs);
379 $err = $this->addDomain($domain, $path);
380 // error raised, break jobs
381 if (PEAR::isError($err)) {
382 return $err;
388 * Add a translation domain.
390 * @param string $domain -- Domain name
391 * @param string $path optional -- Repository path
392 * @throws GetText_Error
394 function addDomain($domain, $path = "./locale/")
396 if (array_key_exists($domain, $this->_domains)) {
397 return;
400 if (!$this->_langCode) {
401 $this->_jobs[] = array($domain, $path);
402 return;
405 $err = $this->_loadDomain($domain, $path);
406 if (PEAR::isError($err)) {
407 return $err;
410 $this->_end++;
414 * Load a translation domain file.
416 * This method cache the translation hash into a php file unless
417 * GETTEXT_NO_CACHE is defined.
419 * @param string $domain -- Domain name
420 * @param string $path optional -- Repository
421 * @throws GetText_Error
422 * @access private
424 function _loadDomain($domain, $path = "./locale")
426 $srcDomain = $path . "/$this->_langCode/LC_MESSAGES/$domain.po";
427 $phpDomain = $path . "/$this->_langCode/LC_MESSAGES/$domain.php";
429 if (!file_exists($srcDomain)) {
430 $str = sprintf('Domain file "%s" not found.', $srcDomain);
431 $err = new GetText_Error($str);
432 return PEAR::raiseError($err);
435 $d = new GetText_Domain();
436 $d->name = $domain;
437 $d->path = $path;
439 if (!file_exists($phpDomain)
440 || (filemtime($phpDomain) < filemtime($srcDomain))) {
442 // parse and compile translation table
443 $parser = new GetText_PHPSupport_Parser();
444 $hash = $parser->parse($srcDomain);
445 if (!defined('GETTEXT_NO_CACHE')) {
446 $comp = new GetText_PHPSupport_Compiler();
447 $err = $comp->compile($hash, $srcDomain);
448 if (PEAR::isError($err)) {
449 return $err;
452 $d->_keys = $hash;
453 } else {
454 $d->_keys = include $phpDomain;
456 $this->_domains[] =& $d;
460 * Implementation of gettext message retrieval.
462 function _getTranslation($key)
464 for ($i = $this->_end; $i >= 0; $i--) {
465 if ($this->_domains[$i]->hasKey($key)) {
466 return $this->_domains[$i]->get($key);
469 return $key;
474 * Class representing a domain file for a specified language.
476 * @access private
477 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
479 class GetText_Domain
481 var $name;
482 var $path;
484 var $_keys = array();
486 function hasKey($key)
488 return array_key_exists($key, $this->_keys);
491 function get($key)
493 return $this->_keys[$key];
498 * This class is used to parse gettext '.po' files into php associative arrays.
500 * @access private
501 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
503 class GetText_PHPSupport_Parser
505 var $_hash = array();
506 var $_currentKey;
507 var $_currentValue;
510 * Parse specified .po file.
512 * @return hashtable
513 * @throws GetText_Error
515 function parse($file)
517 $this->_hash = array();
518 $this->_currentKey = false;
519 $this->_currentValue = "";
521 if (!file_exists($file)) {
522 $str = sprintf('Unable to locate file "%s"', $file);
523 $err = new GetText_Error($str);
524 return PEAR::raiseError($err);
526 $i=0;
527 $lines = file($file);
528 foreach ($lines as $line) {
529 $this->_parseLine($line, ++$i);
531 $this->_storeKey();
533 return $this->_hash;
537 * Parse one po line.
539 * @access private
541 function _parseLine($line, $nbr)
543 if (preg_match('/^\s*?#/', $line)) { return; }
544 if (preg_match('/^\s*?msgid \"(.*?)(?!<\\\)\"/', $line, $m)) {
545 $this->_storeKey();
546 $this->_currentKey = $m[1];
547 return;
549 if (preg_match('/^\s*?msgstr \"(.*?)(?!<\\\)\"/', $line, $m)) {
550 $this->_currentValue .= $m[1];
551 return;
553 if (preg_match('/^\s*?\"(.*?)(?!<\\\)\"/', $line, $m)) {
554 $this->_currentValue .= $m[1];
555 return;
560 * Store last key/value pair into building hashtable.
562 * @access private
564 function _storeKey()
566 if ($this->_currentKey === false) return;
567 $this->_currentValue = str_replace('\\n', "\n", $this->_currentValue);
568 $this->_hash[$this->_currentKey] = $this->_currentValue;
569 $this->_currentKey = false;
570 $this->_currentValue = "";
576 * This class write a php file from a gettext hashtable.
578 * The produced file return the translation hashtable on include.
580 * @throws GetText_Error
581 * @access private
582 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
584 class GetText_PHPSupport_Compiler
587 * Write hash in an includable php file.
589 function compile(&$hash, $sourcePath)
591 $destPath = preg_replace('/\.po$/', '.php', $sourcePath);
592 $fp = @fopen($destPath, "w");
593 if (!$fp) {
594 $str = sprintf('Unable to open "%s" in write mode.', $destPath);
595 $err = new GetText_Error($str);
596 return PEAR::raiseError($err);
598 fwrite($fp, '<?php' . "\n");
599 fwrite($fp, 'return array(' . "\n");
600 foreach ($hash as $key => $value) {
601 $key = str_replace("'", "\\'", $key);
602 $value = str_replace("'", "\\'", $value);
603 fwrite($fp, ' \'' . $key . '\' => \'' . $value . "',\n");
605 fwrite($fp, ');' . "\n");
606 fwrite($fp, '?>');
607 fclose($fp);
612 * GetText related error.
614 class GetText_Error extends PEAR_Error {}