PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/system/application/libraries/MY_Language.php

https://bitbucket.org/zhemel/cloudengine
PHP | 506 lines | 232 code | 57 blank | 217 comment | 40 complexity | 0ad4fff572f4a09e3cdda1698bcb3f40 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /**
  3. * Library to implement gettext-based internationalization.
  4. * Note: Uses php-gettext v1.0.9
  5. * @link http://launchpad.net/php-gettext php-gettext
  6. * @copyright 2009, 2010 The Open University. See CREDITS.txt
  7. * @license http://gnu.org/licenses/gpl-2.0.html GNU GPL v2
  8. * @package I18n
  9. */
  10. require_once APPPATH.'libraries/php-gettext/gettext.inc';
  11. /**
  12. * Extend the Language class to implement Gettext-internationalization.
  13. *
  14. * @link http://gnu.org/software/gettext GNU Gettext
  15. * @link http://launchpad.net/php-gettext php-gettext
  16. */
  17. class My_Language extends CI_Language {
  18. /** The locales array.
  19. *<code>
  20. * $this->locales['en-gb@EXT'] = array('name'=>'English / modified', 'locales'=>..)
  21. *</code>
  22. * @var array Array of locales indexed by code, with names, OS-specific identifiers, and alias-keys.
  23. */
  24. protected $locales = array();
  25. /** @var string ISO language code for user-interface/reading, determined by initialize(). */
  26. protected $lang_ui = NULL;
  27. /** Reference to the CodeIgniter object. */
  28. protected $CI = NULL;
  29. /**
  30. * Set up the initial MY_Language::locales array.
  31. * Class and helper usage:
  32. *<code>
  33. * $LANG =& load_class('Language'); //codeigniter/CodeIgniter.php
  34. *
  35. * $this->obj->lang->initialize(); //app./libraries/Layout::Layout()
  36. *
  37. * <?= t("Photos: !site_link", //app./views/cloud/edit.php
  38. * array('!site_link'=>'<a href="http://flickr.com">Flickr</a>')) ?>
  39. *</code>
  40. *
  41. * @see $locales
  42. */
  43. public function __construct() {
  44. $this->locales = array(
  45. # Keys must be lower-case, using '-'.
  46. # Order is significant - 'en'+aliases must be last.
  47. /* Set in config/cloudengine.php : $config[translations]
  48. # Danish.
  49. 'da' => array(
  50. 'name' => 'Dansk/ Danish',
  51. 'locales'=> array('da_DK.UTF-8', 'da_DK.utf8')
  52. ),*/
  53. # Greek.
  54. 'el' => array(
  55. 'name' => 'Ελληνικά / Greek',
  56. 'locales'=> array(
  57. 'el_GR.UTF-8'/*Mac/10.6*/, 'el_GR.utf8'/*RHEL/Skir*/, 'el_CY.utf8',
  58. 'el_GR@euro',
  59. 'Greek_Greece.x__1253'/*Bug #541: Windows server, we don't want 1253 encoding*/)),
  60. # Greek aliases.
  61. 'el-gr' => 'el',
  62. 'el-cy' => 'el',
  63. # Finally, English.
  64. 'en' => array(
  65. 'name' => 'English',
  66. 'locales'=> array(
  67. 'en_GB.UTF-8', 'en_GB.utf8', 'en_US.UTF-8', 'en_US.utf8', 'English_United Kingdom.1252', 'English_United States')),
  68. #Aliases.
  69. 'en-gb' => 'en',
  70. 'en-us' => 'en',
  71. );
  72. parent::__construct();
  73. log_message('debug', __CLASS__." Class Initialized");
  74. }
  75. /**
  76. * Determine which locale/language to use.
  77. *
  78. * Note, $CI doesn't exist when this class is created (front controller,
  79. * CodeIgniter.php)
  80. * Hence, initialize() is called from app./libraries/Layout.php, not the
  81. * My_Language::__construct.
  82. *
  83. * How the locale/language is chosen:<ol>
  84. *<li> Content negotiation with browser, using 'Accept-Language' HTTP header,
  85. *<li> If there's a COOKIE, use that,
  86. *<li> If there is a POST[lang] HTTP parameter, set a cookie,
  87. *<li> Finally, if there's a GET[lang] HTTP parameter, then override (unless a cookie
  88. * was just set?)
  89. *</ol>
  90. *
  91. * @link http://w3.org/International/questions/qa-accept-lang-locales Accept-Language
  92. * used for locale setting, W3C
  93. * @link http://codeigniter.com/user_guide/libraries/user_agent.html Uses
  94. * CI_User_agent::accept_lang()
  95. * @link http://codeigniter.com/user_guide/libraries/input.html Uses class CI_Input
  96. * @return string The chosen locale.
  97. */
  98. public function initialize() {
  99. $this->CI =& get_instance();
  100. $this->lang_ui = str_replace('english', 'en', $this->CI->config->item('language'));
  101. if (!$this->CI->config->item('x_translate')) {
  102. return FALSE;
  103. }
  104. # Load most locales from config/cloudengine.php file.
  105. $locales_r = $this->CI->config->item('locale_array');
  106. if (is_array($locales_r)) {
  107. $this->locales = array_merge($locales_r, $this->locales);
  108. }
  109. # 1. Content negotiation, using 'Accept-Language' header.
  110. $this->CI->load->library('user_agent');
  111. $_lang = $this->lang_ui;
  112. $method= 'non';
  113. # Order is significant :(
  114. foreach ($this->locales as $lang => $item) {
  115. if ($this->CI->agent->accept_lang(strtolower($lang))) {
  116. # If it's an alias, follow it.
  117. $_lang = is_string($item) ? $item : $lang;
  118. $method= 'ACC';
  119. break;
  120. }
  121. }
  122. # 2. If there's a COOKIE, use that.
  123. $lc = $this->CI->input->cookie('language', FALSE);
  124. if ($lc && isset($this->locales[$lc])) {
  125. if (is_string($this->locales[$lc])) { # It's an alias, follow it.
  126. $lc = $this->locales[$lc];
  127. }
  128. #(And, renew the cookie?)
  129. $_lang = $lc;
  130. $method= 'CKF';
  131. }
  132. # 3. If it's POST[lang], set a cookie (or session?)
  133. $lp = $this->CI->input->post('lang', FALSE);
  134. if ($lp && isset($this->locales[$lp])) {
  135. if (is_string($this->locales[$lp])) {
  136. echo 'MY_Lang, woops, unexpected!';
  137. $lp = $this->locales[$lp];
  138. }
  139. $bok = $this->set_cookie($lp);
  140. $_lang = $lp;
  141. $method= 'CKP';
  142. }
  143. # 4. Finally, if there's a GET[lang], then override (unless a cookie was just set?)
  144. $lg = strtolower(str_replace('_', '-', $this->CI->input->get('lang', FALSE)));
  145. if ($lg && isset($this->locales[$lg])) {
  146. if (is_string($this->locales[$lg])) {
  147. $lg = $this->locales[$lg];
  148. }
  149. if ('CKP'==$method) {
  150. $method = 'CKI';
  151. } else {
  152. $_lang = $lg;
  153. $method = 'GET';
  154. }
  155. }
  156. log_message('debug', "My_Lang: $_lang | how=$method | ".$_SERVER['REQUEST_URI']
  157. ." | ".$this->CI->agent->agent_string()." | ".
  158. $this->accept_lang()." | ".$_SERVER['REMOTE_ADDR']);
  159. $this->lang_ui = $_lang;
  160. $locale_r = $this->locales[$_lang]['locales'];
  161. $this->content_lang($header=TRUE);
  162. return $this->load_gettext($locale_r);
  163. }
  164. /**
  165. * Get the HTTP Accept-Language request header.
  166. *
  167. * @param array $try_langs
  168. * @return boolean
  169. */
  170. protected function accept_lang($try_langs = array()) {
  171. if (! $try_langs) {
  172. return isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ?
  173. $_SERVER['HTTP_ACCEPT_LANGUAGE'] : 'no AL';
  174. }
  175. return NULL;
  176. }
  177. /**
  178. * Set a "language" cookie.
  179. *
  180. * @param string $lang
  181. * @return unknown
  182. */
  183. protected function set_cookie($lang) {
  184. unset($_COOKIE['language']);
  185. $b_cookie=FALSE;
  186. // The expires default is 2 hours. See
  187. // app/config/config.php and app/libs./DB_session.php
  188. $expires = config_item('sess_expiration');
  189. if (is_numeric($expires) && $expires > 0) {
  190. $expires += time();
  191. } else {
  192. $expires = time() + 60*60*2; #7200.
  193. }
  194. #Security/ paranoia: for PHP 5.2+ add $httponly = TURE.
  195. if (phpversion() < 5.2) {
  196. $b_cookie = setcookie('language', $lang, $expires, '/', $dom = NULL, 0);
  197. } else {
  198. $b_cookie = setcookie('language', $lang, $expires, '/', $dom = NULL, 0,
  199. $httponly = TRUE);
  200. }
  201. if ($b_cookie) {
  202. log_message('debug', "My_Lang setcookie: language=$lang");
  203. } else {
  204. log_message('error', "My_Lang setcookie problem");
  205. }
  206. return $b_cookie;
  207. }
  208. /**
  209. * Initialize the gettext library, including setting the locale, and binding the text
  210. * domain (we don't directly "load" a MO binary language file).
  211. * @param array $locale_r Array of OS-specific identifiers for the locale.
  212. * @param string $domain The text domain, "cloudworks" by default.
  213. * @param string $encoding Character encoding.
  214. * @return string The chosen locale.
  215. * @uses T_setlocale
  216. * @uses T_bindtextdomain
  217. */
  218. protected function load_gettext($locale_r, $domain='cloudworks', $encoding='UTF-8') {
  219. // For dates etc. - and to filter arrays!
  220. # Windows hack - don't want "Greek_Greece.1253" :(
  221. $locale = setlocale(LC_TIME, $locale_r);
  222. if (!$locale || false===stripos($locale, '.utf')) {
  223. $locale = $locale_r[0];
  224. }
  225. // php-gettext setup
  226. # T_setlocale doesn't accept arrays :(
  227. $locale = T_setlocale(LC_MESSAGES, $locale);
  228. // Hack: we always want to emulate, so that eg. el_GR.UTF-8 is used correctly.
  229. global $EMULATEGETTEXT;
  230. $EMULATEGETTEXT = 1;
  231. T_bindtextdomain($domain, APPPATH."language");
  232. T_bind_textdomain_codeset($domain, $encoding);
  233. T_textdomain($domain);
  234. return $locale;
  235. }
  236. /**
  237. * Get an array of languages indexed by ISO code, suitable for a form drop-down menu.
  238. * @return array eg. array('el'=>'Greek', 'en'=>'English')
  239. */
  240. public function get_options() {
  241. $options = array();
  242. foreach ($this->locales as $code => $loc) {
  243. if (is_array($loc)) {
  244. # It's not a alias.
  245. $options[$code] = $loc['name'];
  246. }
  247. }
  248. return $options;
  249. }
  250. /**
  251. * Get the ISO language code.
  252. * @return string A language string, eg. "el_GR"
  253. */
  254. public function lang_code() {
  255. return $this->lang_ui;
  256. }
  257. /**
  258. * Send a Content-Language HTTP response header, or return a meta-tag.
  259. * @param bool $header Flag, whether we want a header, default FALSE.
  260. * @return mixed If the $header parameter is TRUE returns void, otherwise returns string
  261. */
  262. public function content_lang($header = FALSE) {
  263. $lang_ui = $this->lang_ui;
  264. if ('en'!=$lang_ui) {
  265. $lang_ui .= ',en';
  266. }
  267. if ($header) {
  268. @header("Content-Language: $lang_ui");
  269. } else {
  270. return '<meta http-equiv="Content-Language" content="'.$lang_ui.'" />'.PHP_EOL;
  271. }
  272. }
  273. /**
  274. * Get a HTML language attribute.
  275. * @return string A language attribute, eg. 'lang="el" '.
  276. */
  277. public function lang_tag() {
  278. return ' lang="'.$this->lang_code().'" ';
  279. }
  280. /**
  281. * Return a HTML <link> to alternative language versions of page.
  282. * @return string
  283. */
  284. public function meta_link() {
  285. return NULL;
  286. }
  287. }
  288. /**
  289. * Translate strings to the page language or a given language.
  290. <code>
  291. <?= ///Translators: this substitutes !title with a dynamic link.
  292. t("Your cloud !title has been created!", //app./views/cloud/cloud_added.php
  293. array('!title' => anchor("cloud/view/$cloud->cloud_id", $cloud->title))) ?>
  294. <?= ///Translators: replaces !site-name! with the predefined $config variable.
  295. t("Visit !site-name!.") ?>
  296. </code>
  297. * @param string $string A string containing the English string to translate.
  298. * @param array $args An associative array of replacements to make after translation.
  299. * Incidences of any key in this array are replaced with the corresponding value. (..):
  300. * !variable: inserted as is (..)
  301. * Reserved keys - see comment below:
  302. * !email! , !required!
  303. *
  304. * @param string $langcode Optional language code to translate to a language other than what is used to display the page.
  305. * @return string The translated string.
  306. * @link http://api.drupal.org/api/function/t Drupal API: 't' function.
  307. * @uses T_gettext()
  308. */
  309. function t($string, $args = array(), $langcode = NULL) {
  310. $msgid = $string;
  311. //BB #173, prevent warnings in log.
  312. if (!$string) return $string;
  313. // Deployment: use the 'php-gettext' emulator.
  314. $string = T_gettext($string);
  315. $CI = & get_instance();
  316. if (FALSE!==$args) {
  317. // Reserved keys - !email! , !required! , !site-name!
  318. $email = $CI->config->item('site_email');
  319. $site_name= $CI->config->item('site_name');
  320. $args = array_merge($args, array(
  321. '[/link]'=> '</a>',
  322. '!email!'=> $email,
  323. '!email-link!'=>"<a href=\"mailto:$email\">$email</a>",
  324. '!site-name!' => $site_name,
  325. '!site-link!' => anchor('', $site_name),
  326. '!required!' => form_required(), //A required form field. (Recurse.)
  327. 'KB' => '<abbr title="'._('Kilo Bytes').'">&thinsp;KB</abbr>',
  328. ));
  329. }
  330. if (FALSE==$args) {
  331. $args = array();
  332. }
  333. if (!$CI->config->item('x_live') && $CI->lang->lang_code()!='en' && $msgid==$string) {
  334. // Debugging: not live and not English - highlight untranslated text (BB issue #139).
  335. // Set a custom attribute 'ut', for styling.
  336. return '<i ut>'.strtr($string, $args).'</i>';
  337. }
  338. return strtr($string, $args);
  339. }
  340. /** Used within t() calls, to replace text like [link-c2525] with the start of a link.
  341. * (See [/link] in t() above).
  342. *
  343. *<code>
  344. * <?= t("You can find answers... in [link-faq]our FAQ[/link].",
  345. * array('[link-faq]' => t_link('about/faq_site'))) ?>
  346. *</code>
  347. *
  348. * @param string A local (relative) or absolute URL.
  349. * @param bool
  350. * @return string An opening tag for a HTML hyperlink.
  351. * @see t()
  352. */
  353. function t_link($url, $local=TRUE) {
  354. if ($local) {
  355. return '<a href="'.site_url($url).'">';
  356. }
  357. return '<a href="'.$url.'">';
  358. }
  359. /** Translate singular-plural strings.
  360. *<code>
  361. * <?= plural(_("!count comment"), _("!count comments"), $row->total_comments) ?>
  362. *</code>
  363. *
  364. * @param string $string1 Singular form, with optional placeholders.
  365. * @param string $string2 Plural form, with optional placeholders.
  366. * Placeholder/ reserved keys - "!count".
  367. * @param int $number Used to determine which form to use.
  368. * @return string The translated string.
  369. * @uses T_ngettext()
  370. */
  371. function plural($string1, $string2, $number) {
  372. $args = array('!count' => $number, '!number' => $number, '!N' => $number);
  373. $string1 = strtr($string1, $args);
  374. $string2 = strtr($string2, $args);
  375. $string = T_ngettext($string1, $string2, $number);
  376. # For the moment, treat as English.
  377. # Plural-Forms: nplurals=2; plural=n == 1 ? 0 : 1;
  378. return $string;
  379. }
  380. /** Translate strings containing a date and/or time.
  381. *<code>
  382. * <?= format_date(_("!event on !date!"), $cloud->modified_date) ?>
  383. *</code>
  384. *
  385. * @param string $format String containing '!date!' or '!date-time!', and optionally other placeholders, like in t().
  386. * @param int $timestamp A Unix timestamp, or uses time() if it's NULL.
  387. * @param array $args An array of arguments, like in t().
  388. * @return string The translated string.
  389. * @uses strftime()
  390. * @uses t()
  391. */
  392. function format_date($format, $timestamp=NULL, $args=array()) {
  393. if (!$timestamp) {
  394. $timestamp = time();
  395. }
  396. if (!$format) {
  397. $format = '!date-time!';
  398. }
  399. $date_args = array(
  400. /*/Translators: date/ date with time format, according to the strftime documentation.
  401. For example, %l (lower-case L) is the hour in 12-hour format (1 through 12).
  402. Eg. "9:49am 7 December 2009". http://php.net/manual/en/function.strftime.php */
  403. '!date-time!' => _("%H:%M on %e %B %Y"), #'g:ia j F Y', content_block.php, zh 2010?3?13? ??? 23:38.
  404. '!date-time-message!' => _("%e %B %Y at %H:%M"), #e.g. 14:24 on 15 Nov 2010
  405. '!date-time-abbr!' => _("%e %b %Y at %H:%M"), #e.g. 14:24 on 15 Nov 2010
  406. /*/Translators: date format, eg. "7 December 2009". */
  407. '!date!' => _("%e %B %Y"), #'j F Y'
  408. '!month-year!' =>_("%B %Y"), #'F Y'
  409. /*/Translators: !month! eg. "December", "Dec" (homepage events block). */
  410. '!month!' => t("%B"),
  411. );
  412. # Hack: '%e' and others don't work in strftime on Windows :(
  413. if (isset($_SERVER['WINDIR'])) {
  414. $date_args = str_replace('%e', '%#d', $date_args);
  415. }
  416. foreach ($date_args as $j => $a) {
  417. $date_args[$j] = strftime($a, $timestamp);
  418. }
  419. $date_args['!date'] = $date_args['!date!'];
  420. $date_args['!event']= NULL;
  421. $args = array_merge($date_args, $args);
  422. return t($format, $args);
  423. }
  424. if (!function_exists('_')) {
  425. /** A no-op function to act as a placeholder in plural()/format_date() function calls.
  426. * @param string A translatable string.
  427. * @return string
  428. * @see plural()
  429. * @see format_date()
  430. */
  431. function _($s) {
  432. return $s;
  433. }
  434. }
  435. /** Text/markup to indicate an optional/required form field, within a <label>.
  436. *<code>
  437. * <?= t("Title !required!") ?>
  438. *</code>
  439. * @param string
  440. * @return string The translated string.
  441. */
  442. function form_required($string = NULL) {
  443. if (!$string) {
  444. $string = t("required", FALSE); #"required";
  445. }
  446. return "($string)";
  447. }