Open Monograph Press  1.1
 All Classes Namespaces Functions Variables Groups Pages
PKPLocale.inc.php
1 <?php
2 
22 import('lib.pkp.classes.i18n.LocaleFile');
23 
24 if (!defined('LOCALE_REGISTRY_FILE')) {
25  define('LOCALE_REGISTRY_FILE', Config::getVar('general', 'registry_dir') . DIRECTORY_SEPARATOR . 'locales.xml');
26 }
27 if (!defined('LOCALE_DEFAULT')) {
28  define('LOCALE_DEFAULT', Config::getVar('i18n', 'locale'));
29 }
30 if (!defined('LOCALE_ENCODING')) {
31  define('LOCALE_ENCODING', Config::getVar('i18n', 'client_charset'));
32 }
33 
34 define('MASTER_LOCALE', 'en_US');
35 
36 // Error types for locale checking.
37 // Note: Cannot use numeric symbols for the constants below because
38 // array_merge_recursive doesn't treat numeric keys nicely.
39 define('LOCALE_ERROR_MISSING_KEY', 'LOCALE_ERROR_MISSING_KEY');
40 define('LOCALE_ERROR_EXTRA_KEY', 'LOCALE_ERROR_EXTRA_KEY');
41 define('LOCALE_ERROR_DIFFERING_PARAMS', 'LOCALE_ERROR_DIFFERING_PARAMS');
42 define('LOCALE_ERROR_MISSING_FILE', 'LOCALE_ERROR_MISSING_FILE');
43 
44 define('EMAIL_ERROR_MISSING_EMAIL', 'EMAIL_ERROR_MISSING_EMAIL');
45 define('EMAIL_ERROR_EXTRA_EMAIL', 'EMAIL_ERROR_EXTRA_EMAIL');
46 define('EMAIL_ERROR_DIFFERING_PARAMS', 'EMAIL_ERROR_DIFFERING_PARAMS');
47 
48 // Shared locale components
49 define('LOCALE_COMPONENT_PKP_COMMON', 0x00000001);
50 define('LOCALE_COMPONENT_PKP_ADMIN', 0x00000002);
51 define('LOCALE_COMPONENT_PKP_INSTALLER', 0x00000003);
52 define('LOCALE_COMPONENT_PKP_MANAGER', 0x00000004);
53 define('LOCALE_COMPONENT_PKP_READER', 0x00000005);
54 define('LOCALE_COMPONENT_PKP_SUBMISSION', 0x00000006);
55 define('LOCALE_COMPONENT_PKP_USER', 0x00000007);
56 define('LOCALE_COMPONENT_PKP_GRID', 0x00000008);
57 define('LOCALE_COMPONENT_PKP_DEFAULT', 0x00000009);
58 define('LOCALE_COMPONENT_PKP_EDITOR', 0x0000000A);
59 define('LOCALE_COMPONENT_PKP_REVIEWER', 0x0000000B);
60 
61 // Application-specific locale components
62 define('LOCALE_COMPONENT_APP_COMMON', 0x00000100);
63 define('LOCALE_COMPONENT_APP_MANAGER', 0x00000101);
64 define('LOCALE_COMPONENT_APP_SUBMISSION', 0x00000102);
65 define('LOCALE_COMPONENT_APP_AUTHOR', 0x00000103);
66 define('LOCALE_COMPONENT_APP_EDITOR', 0x00000104);
67 define('LOCALE_COMPONENT_APP_ADMIN', 0x00000105);
68 define('LOCALE_COMPONENT_APP_DEFAULT', 0x00000106);
69 
70 class PKPLocale {
71  static $request;
72 
78  static function &getLocaleFiles($locale = null) {
79  $localeFiles =& Registry::get('localeFiles', true, array());
80  if ($locale !== null) {
81  if (!isset($localeFiles[$locale])) $localeFiles[$locale] = array();
82  return $localeFiles[$locale];
83  }
84  return $localeFiles;
85  }
86 
96  static function translate($key, $params = array(), $locale = null) {
97  if (!isset($locale)) $locale = AppLocale::getLocale();
98  if (($key = trim($key)) == '') return '';
99 
100  $localeFiles =& AppLocale::getLocaleFiles($locale);
101  $value = '';
102  for ($i = count($localeFiles) - 1 ; $i >= 0 ; $i --) {
103  $value = $localeFiles[$i]->translate($key, $params);
104  if ($value !== null) return $value;
105  }
106 
107  // Add a missing key to the debug notes.
108  $notes =& Registry::get('system.debug.notes');
109  $notes[] = array('debug.notes.missingLocaleKey', array('key' => $key));
110 
111  // Add some octothorpes to missing keys to make them more obvious
112  return '##' . htmlentities($key) . '##';
113  }
114 
119  static function initialize($request) {
120  self::$request = $request;
121 
122  // Use defaults if locale info unspecified.
123  $locale = AppLocale::getLocale();
124 
125  $sysLocale = $locale . '.' . LOCALE_ENCODING;
126  if (!@setlocale(LC_ALL, $sysLocale, $locale)) {
127  // For PHP < 4.3.0
128  if(setlocale(LC_ALL, $sysLocale) != $sysLocale) {
129  setlocale(LC_ALL, $locale);
130  }
131  }
132 
133  AppLocale::registerLocaleFile($locale, "lib/pkp/locale/$locale/common.xml");
134  }
135 
142  static function makeComponentMap($locale) {
143  $baseDir = "lib/pkp/locale/$locale/";
144 
145  return array(
146  LOCALE_COMPONENT_PKP_COMMON => $baseDir . 'common.xml',
147  LOCALE_COMPONENT_PKP_ADMIN => $baseDir . 'admin.xml',
148  LOCALE_COMPONENT_PKP_INSTALLER => $baseDir . 'installer.xml',
149  LOCALE_COMPONENT_PKP_MANAGER => $baseDir . 'manager.xml',
150  LOCALE_COMPONENT_PKP_READER => $baseDir . 'reader.xml',
151  LOCALE_COMPONENT_PKP_SUBMISSION => $baseDir . 'submission.xml',
152  LOCALE_COMPONENT_PKP_EDITOR => $baseDir . 'editor.xml',
153  LOCALE_COMPONENT_PKP_REVIEWER => $baseDir . 'reviewer.xml',
154  LOCALE_COMPONENT_PKP_USER => $baseDir . 'user.xml',
155  LOCALE_COMPONENT_PKP_GRID => $baseDir . 'grid.xml',
156  LOCALE_COMPONENT_PKP_DEFAULT => $baseDir . 'default.xml',
157  );
158  }
159 
165  static function getFilenameComponentMap($locale) {
166  $filenameComponentMap =& Registry::get('localeFilenameComponentMap', true, array());
167  if (!isset($filenameComponentMap[$locale])) {
168  $filenameComponentMap[$locale] = AppLocale::makeComponentMap($locale);
169  }
170  return $filenameComponentMap[$locale];
171  }
172 
178  static function requireComponents() {
179  $params = func_get_args();
180  $paramCount = count($params);
181  if ($paramCount === 0) return;
182 
183  // Get the locale
184  $lastParam = $params[$paramCount-1];
185  if (is_string($lastParam)) {
186  $locale = $lastParam;
187  $paramCount--;
188  } else {
189  $locale = AppLocale::getLocale();
190  }
191 
192  // Backwards compatibility: the list used to be supplied
193  // as an array in the first parameter.
194  if (is_array($params[0])) {
195  $params = $params[0];
196  $paramCount = count($params);
197  }
198 
199  // Go through and make sure each component is loaded if valid.
200  $loadedComponents =& Registry::get('loadedLocaleComponents', true, array());
201  $filenameComponentMap = AppLocale::getFilenameComponentMap($locale);
202  for ($i=0; $i<$paramCount; $i++) {
203  $component = $params[$i];
204 
205  // Don't load components twice
206  if (isset($loadedComponents[$locale][$component])) continue;
207 
208  // Validate component
209  if (!isset($filenameComponentMap[$component])) {
210  fatalError('Unknown locale component ' . $component);
211  }
212 
213  $filename = $filenameComponentMap[$component];
214  AppLocale::registerLocaleFile($locale, $filename);
215  $loadedComponents[$locale][$component] = true;
216  }
217  }
218 
226  static function registerLocaleFile ($locale, $filename, $addToTop = false) {
227  $localeFiles =& AppLocale::getLocaleFiles($locale);
228  $localeFile = new LocaleFile($locale, $filename);
229  if (!$localeFile->isValid()) {
230  return null;
231  }
232  if ($addToTop) {
233  // Work-around: unshift by reference.
234  array_unshift($localeFiles, '');
235  $localeFiles[0] =& $localeFile;
236  } else {
237  $localeFiles[] =& $localeFile;
238  }
239  HookRegistry::call('PKPLocale::registerLocaleFile', array(&$locale, &$filename, &$addToTop));
240  return $localeFile;
241  }
242 
250  static function getLocaleStyleSheet($locale) {
252  if (isset($contents[$locale]['stylesheet'])) {
253  return $contents[$locale]['stylesheet'];
254  }
255  return null;
256  }
257 
263  static function isLocaleComplete($locale) {
265  if (!isset($contents[$locale])) return false;
266  if (isset($contents[$locale]['complete']) && $contents[$locale]['complete'] == 'false') {
267  return false;
268  }
269  return true;
270  }
271 
277  static function isLocaleValid($locale) {
278  if (empty($locale)) return false;
279  if (!preg_match('/^[a-z][a-z]_[A-Z][A-Z]$/', $locale)) return false;
280  if (file_exists('locale/' . $locale)) return true;
281  return false;
282  }
283 
289  static function &loadLocaleList($filename) {
290  $xmlDao = new XMLDAO();
291  $data = $xmlDao->parseStruct($filename, array('locale'));
292  $allLocales = array();
293 
294  // Build array with ($localKey => $localeName)
295  if (isset($data['locale'])) {
296  foreach ($data['locale'] as $localeData) {
297  $allLocales[$localeData['attributes']['key']] = $localeData['attributes'];
298  }
299  }
300 
301  return $allLocales;
302  }
303 
308  static function &getAllLocales() {
309  $rawContents =& AppLocale::_getAllLocalesCacheContent();
310  $allLocales = array();
311 
312  foreach ($rawContents as $locale => $contents) {
313  $allLocales[$locale] = $contents['name'];
314  }
315 
316  // if client encoding is set to iso-8859-1, transcode locales from utf8
317  if (LOCALE_ENCODING == "iso-8859-1") {
318  $allLocales = array_map('utf8_decode', $allLocales);
319  }
320 
321  return $allLocales;
322  }
323 
328  static function installLocale($locale) {
329  // Install default locale-specific data
330  import('lib.pkp.classes.db.DBDataXMLParser');
331 
332  $emailTemplateDao = DAORegistry::getDAO('EmailTemplateDAO');
333  $emailTemplateDao->installEmailTemplateData($emailTemplateDao->getMainEmailTemplateDataFilename($locale));
334 
335  // Load all plugins so they can add locale data if needed
336  $categories = PluginRegistry::getCategories();
337  foreach ($categories as $category) {
338  PluginRegistry::loadCategory($category);
339  }
340  HookRegistry::call('PKPLocale::installLocale', array(&$locale));
341  }
342 
347  static function uninstallLocale($locale) {
348  // Delete locale-specific data
349  $emailTemplateDao = DAORegistry::getDAO('EmailTemplateDAO');
350  $emailTemplateDao->deleteEmailTemplatesByLocale($locale);
351  $emailTemplateDao->deleteDefaultEmailTemplatesByLocale($locale);
352  }
353 
358  static function reloadLocale($locale) {
360  AppLocale::installLocale($locale);
361  }
362 
369  static function getParameterNames($source) {
370  $matches = null;
371  String::regexp_match_all('/({\$[^}]+})/' /* '/{\$[^}]+})/' */, $source, $matches);
372  array_shift($matches); // Knock the top element off the array
373  if (isset($matches[0])) return $matches[0];
374  return array();
375  }
376 
384  static function get3LetterFrom2LetterIsoLanguage($iso2Letter) {
385  assert(strlen($iso2Letter) == 2);
387  foreach($locales as $locale => $localeData) {
388  if (substr($locale, 0, 2) == $iso2Letter) {
389  assert(isset($localeData['iso639-2b']));
390  return $localeData['iso639-2b'];
391  }
392  }
393  return null;
394  }
395 
403  static function get2LetterFrom3LetterIsoLanguage($iso3Letter) {
404  assert(strlen($iso3Letter) == 3);
406  foreach($locales as $locale => $localeData) {
407  assert(isset($localeData['iso639-2b']));
408  if ($localeData['iso639-2b'] == $iso3Letter) {
409  return substr($locale, 0, 2);
410  }
411  }
412  return null;
413  }
414 
421  static function get3LetterIsoFromLocale($locale) {
422  assert(strlen($locale) == 5);
423  $iso2Letter = substr($locale, 0, 2);
425  }
426 
441  static function getLocaleFrom3LetterIso($iso3Letter) {
442  assert(strlen($iso3Letter) == 3);
443  $primaryLocale = AppLocale::getPrimaryLocale();
444 
445  $localeCandidates = array();
447  foreach($locales as $locale => $localeData) {
448  assert(isset($localeData['iso639-2b']));
449  if ($localeData['iso639-2b'] == $iso3Letter) {
450  if ($locale == $primaryLocale) {
451  // In case of ambiguity the primary locale
452  // overrides all other options so we're done.
453  return $primaryLocale;
454  }
455  $localeCandidates[] = $locale;
456  }
457  }
458 
459  // Return null if we found no candidate locale.
460  if (empty($localeCandidates)) return null;
461 
462  if (count($localeCandidates) > 1) {
463  // Check whether one of the candidate locales
464  // is a supported locale. If so choose the first
465  // supported locale.
466  $supportedLocales = AppLocale::getSupportedLocales();
467  foreach($supportedLocales as $supportedLocale => $localeName) {
468  if (in_array($supportedLocale, $localeCandidates)) return $supportedLocale;
469  }
470  }
471 
472  // If there is only one candidate (or if we were
473  // unable to disambiguate) then return the unique
474  // (first) candidate found.
475  return array_shift($localeCandidates);
476  }
477 
484  static function getIso3FromIso1($iso1) {
485  assert(strlen($iso1) == 2);
487  foreach($locales as $locale => $localeData) {
488  if (substr($locale, 0, 2) == $iso1) {
489  assert(isset($localeData['iso639-3']));
490  return $localeData['iso639-3'];
491  }
492  }
493  return null;
494  }
495 
502  static function getIso1FromIso3($iso3) {
503  assert(strlen($iso3) == 3);
505  foreach($locales as $locale => $localeData) {
506  assert(isset($localeData['iso639-3']));
507  if ($localeData['iso639-3'] == $iso3) {
508  return substr($locale, 0, 2);
509  }
510  }
511  return null;
512  }
513 
520  static function getIso3FromLocale($locale) {
521  assert(strlen($locale) == 5);
522  $iso1 = substr($locale, 0, 2);
523  return AppLocale::getIso3FromIso1($iso1);
524  }
525 
532  static function getIso1FromLocale($locale) {
533  assert(strlen($locale) == 5);
534  return substr($locale, 0, 2);
535  }
536 
551  static function getLocaleFromIso3($iso3) {
552  assert(strlen($iso3) == 3);
553  $primaryLocale = AppLocale::getPrimaryLocale();
554 
555  $localeCandidates = array();
557  foreach($locales as $locale => $localeData) {
558  assert(isset($localeData['iso639-3']));
559  if ($localeData['iso639-3'] == $iso3) {
560  if ($locale == $primaryLocale) {
561  // In case of ambiguity the primary locale
562  // overrides all other options so we're done.
563  return $primaryLocale;
564  }
565  $localeCandidates[] = $locale;
566  }
567  }
568 
569  // Return null if we found no candidate locale.
570  if (empty($localeCandidates)) return null;
571 
572  if (count($localeCandidates) > 1) {
573  // Check whether one of the candidate locales
574  // is a supported locale. If so choose the first
575  // supported locale.
576  $supportedLocales = AppLocale::getSupportedLocales();
577  foreach($supportedLocales as $supportedLocale => $localeName) {
578  if (in_array($supportedLocale, $localeCandidates)) return $supportedLocale;
579  }
580  }
581 
582  // If there is only one candidate (or if we were
583  // unable to disambiguate) then return the unique
584  // (first) candidate found.
585  return array_shift($localeCandidates);
586  }
587 
588  //
589  // Private helper methods.
590  //
595  static function &_getAllLocalesCacheContent() {
596  static $contents = false;
597  if ($contents === false) {
598  $allLocalesCache =& AppLocale::_getAllLocalesCache();
599  $contents = $allLocalesCache->getContents();
600  }
601  return $contents;
602  }
603 
608  static function &_getAllLocalesCache() {
609  $cache =& Registry::get('allLocalesCache', true, null);
610  if ($cache === null) {
611  $cacheManager = CacheManager::getManager();
612  $cache = $cacheManager->getFileCache(
613  'locale', 'list',
614  array('AppLocale', '_allLocalesCacheMiss')
615  );
616 
617  // Check to see if the data is outdated
618  $cacheTime = $cache->getCacheTime();
619  if ($cacheTime !== null && $cacheTime < filemtime(LOCALE_REGISTRY_FILE)) {
620  $cache->flush();
621  }
622  }
623  return $cache;
624  }
625 
631  static function _allLocalesCacheMiss($cache, $id) {
632  $allLocales =& Registry::get('allLocales', true, null);
633  if ($allLocales === null) {
634  // Add a locale load to the debug notes.
635  $notes =& Registry::get('system.debug.notes');
636  $notes[] = array('debug.notes.localeListLoad', array('localeList' => LOCALE_REGISTRY_FILE));
637 
638  // Reload locale registry file
639  $allLocales = AppLocale::loadLocaleList(LOCALE_REGISTRY_FILE);
640  asort($allLocales);
641  $cache->setEntireCache($allLocales);
642  }
643  return null;
644  }
645 
650  static function getTimeZone() {
651  $timeZone = null;
652 
653  // Load the time zone from the configuration file
654  if ($timeZoneConfig = Config::getVar('general', 'time_zone')) {
655  $timeZoneDAO = DAORegistry::getDAO('TimeZoneDAO');
656  $timeZoneList = $timeZoneDAO->getTimeZones();
657  foreach ($timeZoneList as $timeZoneKey => $timeZoneName) {
658  if (in_array($timeZoneConfig, array($timeZoneKey, $timeZoneName))) {
659  $timeZone = $timeZoneKey;
660  break;
661  }
662  }
663  }
664 
665  // Fall back to the time zone set in php.ini
666  if (empty($timeZone)) $timeZone = ini_get('date.timezone');
667 
668  // Fall back to UTC
669  if (empty($timeZone)) $timeZone = 'UTC';
670 
671  return $timeZone;
672  }
673 }
674 
675 
693 function __($key, $params = array(), $locale = null) {
694  return AppLocale::translate($key, $params, $locale);
695 }
696 
697 ?>
static translate($key, $params=array(), $locale=null)
static & getDAO($name, $dbconn=null)
static get3LetterFrom2LetterIsoLanguage($iso2Letter)
static get3LetterIsoFromLocale($locale)
static & _getAllLocalesCache()
static installLocale($locale)
static getPrimaryLocale()
static getFilenameComponentMap($locale)
static getLocaleFromIso3($iso3)
static & loadCategory($category, $enabledOnly=false, $mainContextId=null)
Abstraction of a locale file.
static getSupportedLocales()
static & getLocaleFiles($locale=null)
static reloadLocale($locale)
static getLocaleFrom3LetterIso($iso3Letter)
Operations for retrieving and modifying objects from an XML data source.
Definition: XMLDAO.inc.php:19
Provides methods for loading locale data and translating strings identified by unique keys...
static & getAllLocales()
static getLocale()
static getVar($section, $key, $default=null)
Definition: Config.inc.php:35
static getIso3FromIso1($iso1)
static call($hookName, $args=null)
static get2LetterFrom3LetterIsoLanguage($iso3Letter)
static installLocale($locale)
static isLocaleComplete($locale)
static _allLocalesCacheMiss($cache, $id)
static getLocaleStyleSheet($locale)
static uninstallLocale($locale)
static getIso3FromLocale($locale)
static getIso1FromIso3($iso3)
static getTimeZone()
static & get($key, $createIfEmpty=false, $createWithDefault=null)
static & loadLocaleList($filename)
static makeComponentMap($locale)
static uninstallLocale($locale)
static requireComponents()
static isLocaleValid($locale)
static translate($key, $params=array(), $locale=null)
static getIso1FromLocale($locale)
static registerLocaleFile($locale, $filename, $addToTop=false)
static regexp_match_all($pattern, $subject, &$matches)
Definition: String.inc.php:364
static & _getAllLocalesCacheContent()
static makeComponentMap($locale)
static getParameterNames($source)
static initialize($request)