<?php
/**
* @defgroup i18n I18N
* Implements localization concerns such as locale files, time zones, and country lists.
*/
/**
* @file classes/i18n/PKPLocale.inc.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPLocale
* @ingroup i18n
*
* @brief Provides methods for loading locale data and translating strings identified by unique keys
*/
import('lib.pkp.classes.i18n.LocaleFile');
if (!defined('LOCALE_REGISTRY_FILE')) {
define('LOCALE_REGISTRY_FILE', 'registry/locales.xml');
}
if (!defined('LOCALE_DEFAULT')) {
define('LOCALE_DEFAULT', Config::getVar('i18n', 'locale'));
}
if (!defined('LOCALE_ENCODING')) {
define('LOCALE_ENCODING', Config::getVar('i18n', 'client_charset'));
}
define('MASTER_LOCALE', 'en_US');
// Error types for locale checking.
// Note: Cannot use numeric symbols for the constants below because
// array_merge_recursive doesn't treat numeric keys nicely.
define('LOCALE_ERROR_MISSING_KEY', 'LOCALE_ERROR_MISSING_KEY');
define('LOCALE_ERROR_EXTRA_KEY', 'LOCALE_ERROR_EXTRA_KEY');
define('LOCALE_ERROR_DIFFERING_PARAMS', 'LOCALE_ERROR_DIFFERING_PARAMS');
define('LOCALE_ERROR_MISSING_FILE', 'LOCALE_ERROR_MISSING_FILE');
define('EMAIL_ERROR_MISSING_EMAIL', 'EMAIL_ERROR_MISSING_EMAIL');
define('EMAIL_ERROR_EXTRA_EMAIL', 'EMAIL_ERROR_EXTRA_EMAIL');
define('EMAIL_ERROR_DIFFERING_PARAMS', 'EMAIL_ERROR_DIFFERING_PARAMS');
// Shared locale components
define('LOCALE_COMPONENT_PKP_COMMON', 0x00000001);
define('LOCALE_COMPONENT_PKP_ADMIN', 0x00000002);
define('LOCALE_COMPONENT_PKP_INSTALLER', 0x00000003);
define('LOCALE_COMPONENT_PKP_MANAGER', 0x00000004);
define('LOCALE_COMPONENT_PKP_READER', 0x00000005);
define('LOCALE_COMPONENT_PKP_SUBMISSION', 0x00000006);
define('LOCALE_COMPONENT_PKP_USER', 0x00000007);
define('LOCALE_COMPONENT_PKP_GRID', 0x00000008);
define('LOCALE_COMPONENT_PKP_DEFAULT', 0x00000009);
define('LOCALE_COMPONENT_PKP_EDITOR', 0x0000000A);
define('LOCALE_COMPONENT_PKP_REVIEWER', 0x0000000B);
define('LOCALE_COMPONENT_PKP_API', 0x0000000C);
// Application-specific locale components
define('LOCALE_COMPONENT_APP_COMMON', 0x00000100);
define('LOCALE_COMPONENT_APP_MANAGER', 0x00000101);
define('LOCALE_COMPONENT_APP_SUBMISSION', 0x00000102);
define('LOCALE_COMPONENT_APP_AUTHOR', 0x00000103);
define('LOCALE_COMPONENT_APP_EDITOR', 0x00000104);
define('LOCALE_COMPONENT_APP_ADMIN', 0x00000105);
define('LOCALE_COMPONENT_APP_DEFAULT', 0x00000106);
define('LOCALE_COMPONENT_APP_API', 0x00000107);
define('LOCALE_COMPONENT_APP_EMAIL', 0x00000108);
use Illuminate\Database\Capsule\Manager as Capsule;
class PKPLocale {
static $request;
/**
* Get all supported UI locales for the current context.
* @return array
*/
static function getSupportedLocales() {
static $supportedLocales;
if (!isset($supportedLocales)) {
if (defined('SESSION_DISABLE_INIT')) {
$supportedLocales = AppLocale::getAllLocales();
} elseif (($context = self::$request->getContext())) {
$supportedLocales = $context->getSupportedLocaleNames();
} else {
$site = self::$request->getSite();
$supportedLocales = $site->getSupportedLocaleNames();
}
}
return $supportedLocales;
}
/**
* Get all supported form locales for the current context.
* @return array
*/
static function getSupportedFormLocales() {
static $supportedFormLocales;
if (!isset($supportedFormLocales)) {
if (defined('SESSION_DISABLE_INIT')) {
$supportedFormLocales = AppLocale::getAllLocales();
} elseif (($context = self::$request->getContext())) {
$supportedFormLocales = $context->getSupportedFormLocaleNames();
} else {
$site = self::$request->getSite();
$supportedFormLocales = $site->getSupportedLocaleNames();
}
}
return $supportedFormLocales;
}
/**
* Return the key name of the user's currently selected locale (default
* is "en_US" for U.S. English).
* @return string
*/
static function getLocale() {
static $currentLocale;
if (!isset($currentLocale)) {
if (defined('SESSION_DISABLE_INIT')) {
// If the locale is specified in the URL, allow
// it to override. (Necessary when locale is
// being set, as cookie will not yet be re-set)
$locale = self::$request->getUserVar('setLocale');
if (empty($locale) || !in_array($locale, array_keys(AppLocale::getSupportedLocales()))) $locale = self::$request->getCookieVar('currentLocale');
} else {
$sessionManager = SessionManager::getManager();
$session = $sessionManager->getUserSession();
$locale = self::$request->getUserVar('uiLocale');
$context = self::$request->getContext();
$site = self::$request->getSite();
if (!isset($locale)) {
$locale = $session->getSessionVar('currentLocale');
}
if (!isset($locale)) {
$locale = self::$request->getCookieVar('currentLocale');
}
if (isset($locale)) {
// Check if user-specified locale is supported
if ($context != null) {
$locales = $context->getSupportedLocaleNames();
} else {
$locales = $site->getSupportedLocaleNames();
}
if (!in_array($locale, array_keys($locales))) {
unset($locale);
}
}
if (!isset($locale)) {
// Use context/site default
if ($context != null) {
$locale = $context->getPrimaryLocale();
}
if (!isset($locale)) {
$locale = $site->getPrimaryLocale();
}
}
}
if (!AppLocale::isLocaleValid($locale)) {
$locale = LOCALE_DEFAULT;
}
$currentLocale = $locale;
}
return $currentLocale;
}
/**
* Get the stack of "important" locales, most important first.
* @return array
*/
static function getLocalePrecedence() {
static $localePrecedence;
if (!isset($localePrecedence)) {
$localePrecedence = array(AppLocale::getLocale());
$context = self::$request->getContext();
if ($context && !in_array($context->getPrimaryLocale(), $localePrecedence)) $localePrecedence[] = $context->getPrimaryLocale();
$site = self::$request->getSite();
if ($site && !in_array($site->getPrimaryLocale(), $localePrecedence)) $localePrecedence[] = $site->getPrimaryLocale();
}
return $localePrecedence;
}
/**
* Retrieve the primary locale of the current context.
* @return string
*/
static function getPrimaryLocale() {
static $locale;
if ($locale) return $locale;
if (defined('SESSION_DISABLE_INIT')) return $locale = LOCALE_DEFAULT;
$context = self::$request->getContext();
if (isset($context)) {
$locale = $context->getPrimaryLocale();
}
if (!isset($locale)) {
$site = self::$request->getSite();
$locale = $site->getPrimaryLocale();
}
if (!isset($locale) || !AppLocale::isLocaleValid($locale)) {
$locale = LOCALE_DEFAULT;
}
return $locale;
}
/**
* Get a list of locale files currently registered, either in all
* locales (in an array for each locale), or for a specific locale.
* @param $locale string Locale identifier (optional)
*/
static function &getLocaleFiles($locale = null) {
$localeFiles =& Registry::get('localeFiles', true, array());
if ($locale !== null) {
if (!isset($localeFiles[$locale])) $localeFiles[$locale] = array();
return $localeFiles[$locale];
}
return $localeFiles;
}
/**
* Add octothorpes to a key name for presentation of the key as missing.
* @param @key string
* @return string
*/
public static function addOctothorpes($key) {
return '##' . htmlentities($key) . '##';
}
/**
* Translate a string using the selected locale.
* Substitution works by replacing tokens like "{$foo}" with the value
* of the parameter named "foo" (if supplied).
* @param $key string
* @param $params array named substitution parameters
* @param $locale string the locale to use
* @param $missingKeyHandler function Callback to be invoked when a key cannot be found.
* @return string
*/
static function translate($key, $params = array(), $locale = null, $missingKeyHandler = array(__CLASS__, 'addOctothorpes')) {
if (!isset($locale)) $locale = AppLocale::getLocale();
if (($key = trim($key)) == '') return '';
$localeFiles =& AppLocale::getLocaleFiles($locale);
$value = '';
for ($i = count($localeFiles) - 1 ; $i >= 0 ; $i --) {
$value = $localeFiles[$i]->translate($key, $params);
if ($value !== null) return $value;
}
// Add a missing key to the debug notes.
$notes =& Registry::get('system.debug.notes');
$notes[] = array('debug.notes.missingLocaleKey', array('key' => $key));
if (!HookRegistry::call('PKPLocale::translate', array(&$key, &$params, &$locale, &$localeFiles, &$value))) {
// Add some octothorpes to missing keys to make them more obvious
return $missingKeyHandler($key);
} else {
return $value;
}
}
/**
* Initialize the locale system.
* @param $request PKPRequest
*/
static function initialize($request) {
self::$request = $request;
// Use defaults if locale info unspecified.
$locale = AppLocale::getLocale();
setlocale(LC_ALL, $locale . '.' . LOCALE_ENCODING, $locale);
putenv("LC_ALL=$locale");
AppLocale::registerLocaleFile($locale, "lib/pkp/locale/$locale/common.po");
// Set site time zone
// Starting from PHP 5.3.0 PHP will throw an E_WARNING if the default
// time zone is not set and date/time functions are used
// http://pl.php.net/manual/en/function.date-default-timezone-set.php
$timeZone = self::getTimeZone();
date_default_timezone_set($timeZone);
if (Config::getVar('general', 'installed')) {
// Set the time zone for DB
// Get the offset from UTC
$now = new DateTime();
$mins = $now->getOffset() / 60;
$sgn = ($mins < 0 ? -1 : 1);
$mins = abs($mins);
$hrs = floor($mins / 60);
$mins -= $hrs * 60;
$offset = sprintf('%+d:%02d', $hrs*$sgn, $mins);
switch(Config::getVar('database', 'driver')) {
case 'mysql':
case 'mysqli':
Capsule::statement('SET time_zone = \''.$offset.'\'');
break;
case 'postgres':
case 'postgres64':
case 'postgres7':
case 'postgres8':
case 'postgres9':
Capsule::statement('SET TIME ZONE INTERVAL \''.$offset.'\' HOUR TO MINUTE');
break;
default: assert(false);
}
}
}
/**
* Build an associative array of LOCALE_COMPOMENT_... => filename
* (use getFilenameComponentMap instead)
* @param $locale string
* @return array
*/
static function makeComponentMap($locale) {
$baseDir = "lib/pkp/locale/$locale/";
return array(
LOCALE_COMPONENT_PKP_COMMON => $baseDir . 'common.po',
LOCALE_COMPONENT_PKP_ADMIN => $baseDir . 'admin.po',
LOCALE_COMPONENT_PKP_INSTALLER => $baseDir . 'installer.po',
LOCALE_COMPONENT_PKP_MANAGER => $baseDir . 'manager.po',
LOCALE_COMPONENT_PKP_READER => $baseDir . 'reader.po',
LOCALE_COMPONENT_PKP_SUBMISSION => $baseDir . 'submission.po',
LOCALE_COMPONENT_PKP_EDITOR => $baseDir . 'editor.po',
LOCALE_COMPONENT_PKP_REVIEWER => $baseDir . 'reviewer.po',
LOCALE_COMPONENT_PKP_USER => $baseDir . 'user.po',
LOCALE_COMPONENT_PKP_GRID => $baseDir . 'grid.po',
LOCALE_COMPONENT_PKP_DEFAULT => $baseDir . 'default.po',
LOCALE_COMPONENT_PKP_API => $baseDir . 'api.po',
);
}
/**
* Get an associative array of LOCALE_COMPOMENT_... => filename
* @param $locale string
* @return array
*/
static function getFilenameComponentMap($locale) {
$filenameComponentMap =& Registry::get('localeFilenameComponentMap', true, array());
if (!isset($filenameComponentMap[$locale])) {
$filenameComponentMap[$locale] = AppLocale::makeComponentMap($locale);
}
return $filenameComponentMap[$locale];
}
/**
* Load a set of locale components. Parameters of mixed length may
* be supplied, each a LOCALE_COMPONENT_... constant. An optional final
* parameter may be supplied to specify the locale (e.g. 'en_US').
*/
static function requireComponents() {
$params = func_get_args();
$paramCount = count($params);
if ($paramCount === 0) return;
// Get the locale
$lastParam = $params[$paramCount-1];
if (is_string($lastParam)) {
$locale = $lastParam;
$paramCount--;
} else {
$locale = AppLocale::getLocale();
}
// Backwards compatibility: the list used to be supplied
// as an array in the first parameter.
if (is_array($params[0])) {
$params = $params[0];
$paramCount = count($params);
}
// Go through and make sure each component is loaded if valid.
$loadedComponents =& Registry::get('loadedLocaleComponents', true, array());
$filenameComponentMap = AppLocale::getFilenameComponentMap($locale);
for ($i=0; $i<$paramCount; $i++) {
$component = $params[$i];
// Don't load components twice
if (isset($loadedComponents[$locale][$component])) continue;
// Validate component
if (!isset($filenameComponentMap[$component])) {
fatalError('Unknown locale component ' . $component);
}
$filename = $filenameComponentMap[$component];
AppLocale::registerLocaleFile($locale, $filename);
$loadedComponents[$locale][$component] = true;
}
}
/**
* Register a locale file against the current list.
* @param $locale string Locale key
* @param $filename string Filename to new locale XML file
* @param $addToTop boolean Whether to add to the top of the list (true)
* or the bottom (false). Allows overriding.
*/
static function registerLocaleFile ($locale, $filename, $addToTop = false) {
$localeFiles =& AppLocale::getLocaleFiles($locale);
$localeFile = new LocaleFile($locale, $filename);
if (!HookRegistry::call('PKPLocale::registerLocaleFile::isValidLocaleFile', array(&$localeFile))) {
if (!$localeFile->isValid()) return null;
}
if ($addToTop) {
// Work-around: unshift by reference.
array_unshift($localeFiles, '');
$localeFiles[0] =& $localeFile;
} else {
$localeFiles[] =& $localeFile;
}
HookRegistry::call('PKPLocale::registerLocaleFile', array(&$locale, &$filename, &$addToTop));
return $localeFile;
}
/**
* Get the stylesheet filename for a particular locale.
* @param $locale string
* @return string or null if none configured.
*/
static function getLocaleStyleSheet($locale) {
$contents =& AppLocale::_getAllLocalesCacheContent();
if (isset($contents[$locale]['stylesheet'])) {
return $contents[$locale]['stylesheet'];
}
return null;
}
/**
* Get the reading direction for a particular locale.
*
* A locale can specify a reading direction with the `direction` attribute. If no
* direction is specified, defaults to `ltr` (left-to-right). The only
* other value that is expected is `rtl`. This value is used in HTML and
* CSS markup to present a right-to-left layout.
*
* @param $locale string
* @return string
*/
static function getLocaleDirection($locale) {
$contents =& AppLocale::_getAllLocalesCacheContent();
if (isset($contents[$locale]['direction'])) {
return $contents[$locale]['direction'];
}
return 'ltr';
}
/**
* Determine whether or not a locale is marked incomplete.
* @param $locale xx_XX symbolic name of locale to check
* @return boolean
*/
static function isLocaleComplete($locale) {
$contents =& AppLocale::_getAllLocalesCacheContent();
if (!isset($contents[$locale])) return false;
if (isset($contents[$locale]['complete']) && $contents[$locale]['complete'] == 'false') {
return false;
}
return true;
}
/**
* Determine whether or not a locale uses family name first.
* @param $locale xx_XX symbolic name of locale to check
* @return boolean
*/
static function isLocaleWithFamilyFirst($locale) {
$contents =& AppLocale::_getAllLocalesCacheContent();
if (isset($contents[$locale]) && isset($contents[$locale]['familyFirst']) && $contents[$locale]['familyFirst'] == 'true') {
return true;
}
return false;
}
/**
* Check if the supplied locale is currently installable.
* @param $locale string
* @return boolean
*/
static function isLocaleValid($locale) {
if (empty($locale)) return false;
// variants can be composed of five to eight letters, or of four characters starting with a digit
if (!preg_match('/^[a-z][a-z]_[A-Z][A-Z](@([A-Za-z0-9]{5,8}|\d[A-Za-z0-9]{3}))?$/', $locale)) return false;
if (file_exists('locale/' . $locale)) return true;
return false;
}
/**
* Load a locale list from a file.
* @param $filename string
* @return array
*/
static function &loadLocaleList($filename) {
$xmlDao = new XMLDAO();
$data = $xmlDao->parseStruct($filename, array('locale'));
$allLocales = array();
// Build array with ($localKey => $localeName)
if (isset($data['locale'])) {
foreach ($data['locale'] as $localeData) {
$allLocales[$localeData['attributes']['key']] = $localeData['attributes'];
}
}
return $allLocales;
}
/**
* Return a list of all available locales.
* @return array
*/
static function &getAllLocales() {
$rawContents =& AppLocale::_getAllLocalesCacheContent();
$allLocales = array();
foreach ($rawContents as $locale => $contents) {
$allLocales[$locale] = $contents['name'];
}
// if client encoding is set to iso-8859-1, transcode locales from utf8
if (LOCALE_ENCODING == "iso-8859-1") {
$allLocales = array_map('utf8_decode', $allLocales);
}
return $allLocales;
}
/**
* Install support for a new locale.
* @param $locale string
*/
static function installLocale($locale) {
// Install default locale-specific data
import('lib.pkp.classes.db.DBDataXMLParser');
$emailTemplateDao = DAORegistry::getDAO('EmailTemplateDAO'); /* @var $emailTemplateDao EmailTemplateDAO */
AppLocale::requireComponents(LOCALE_COMPONENT_APP_EMAIL, $locale);
$emailTemplateDao->installEmailTemplateLocaleData($emailTemplateDao->getMainEmailTemplatesFilename(), array($locale));
// Load all plugins so they can add locale data if needed
$categories = PluginRegistry::getCategories();
foreach ($categories as $category) {
PluginRegistry::loadCategory($category);
}
HookRegistry::call('PKPLocale::installLocale', array(&$locale));
}
/**
* Uninstall support for an existing locale.
* @param $locale string
*/
static function uninstallLocale($locale) {
// Delete locale-specific data
$emailTemplateDao = DAORegistry::getDAO('EmailTemplateDAO'); /* @var $emailTemplateDao EmailTemplateDAO */
$emailTemplateDao->deleteEmailTemplatesByLocale($locale);
$emailTemplateDao->deleteDefaultEmailTemplatesByLocale($locale);
}
/**
* Reload locale-specific data.
* @param $locale string
*/
static function reloadLocale($locale) {
AppLocale::installLocale($locale);
}
/**
* Given a locale string, get the list of parameter references of the
* form {$myParameterName}.
* @param $source string
* @return array
*/
static function getParameterNames($source) {
$matches = null;
PKPString::regexp_match_all('/({\$[^}]+})/' /* '/{\$[^}]+})/' */, $source, $matches);
array_shift($matches); // Knock the top element off the array
if (isset($matches[0])) return $matches[0];
return array();
}
/**
* Translate the ISO 2-letter language string (ISO639-1)
* into a ISO compatible 3-letter string (ISO639-2b).
* @param $iso2Letter string
* @return string the translated string or null if we
* don't know about the given language.
*/
static function get3LetterFrom2LetterIsoLanguage($iso2Letter) {
assert(strlen($iso2Letter) == 2);
$locales =& AppLocale::_getAllLocalesCacheContent();
foreach($locales as $locale => $localeData) {
if (substr($locale, 0, 2) == $iso2Letter) {
assert(isset($localeData['iso639-2b']));
return $localeData['iso639-2b'];
}
}
return null;
}
/**
* Translate the ISO 3-letter language string (ISO639-2b)
* into a ISO compatible 2-letter string (ISO639-1).
* @param $iso3Letter string
* @return string the translated string or null if we
* don't know about the given language.
*/
static function get2LetterFrom3LetterIsoLanguage($iso3Letter) {
assert(strlen($iso3Letter) == 3);
$locales =& AppLocale::_getAllLocalesCacheContent();
foreach($locales as $locale => $localeData) {
assert(isset($localeData['iso639-2b']));
if ($localeData['iso639-2b'] == $iso3Letter) {
return substr($locale, 0, 2);
}
}
return null;
}
/**
* Translate the PKP locale identifier into an
* ISO639-2b compatible 3-letter string.
* @param $locale string
* @return string
*/
static function get3LetterIsoFromLocale($locale) {
assert(strlen($locale) >= 5);
$iso2Letter = substr($locale, 0, 2);
return AppLocale::get3LetterFrom2LetterIsoLanguage($iso2Letter);
}
/**
* Translate an ISO639-2b compatible 3-letter string
* into the PKP locale identifier.
*
* This can be ambiguous if several locales are defined
* for the same language. In this case we'll use the
* primary locale to disambiguate.
*
* If that still doesn't determine a unique locale then
* we'll choose the first locale found.
*
* @param $iso3letter string
* @return string
*/
static function getLocaleFrom3LetterIso($iso3Letter) {
assert(strlen($iso3Letter) == 3);
$primaryLocale = AppLocale::getPrimaryLocale();
$localeCandidates = array();
$locales =& AppLocale::_getAllLocalesCacheContent();
foreach($locales as $locale => $localeData) {
assert(isset($localeData['iso639-2b']));
if ($localeData['iso639-2b'] == $iso3Letter) {
if ($locale == $primaryLocale) {
// In case of ambiguity the primary locale
// overrides all other options so we're done.
return $primaryLocale;
}
$localeCandidates[] = $locale;
}
}
// Return null if we found no candidate locale.
if (empty($localeCandidates)) return null;
if (count($localeCandidates) > 1) {
// Check whether one of the candidate locales
// is a supported locale. If so choose the first
// supported locale.
$supportedLocales = AppLocale::getSupportedLocales();
foreach($supportedLocales as $supportedLocale => $localeName) {
if (in_array($supportedLocale, $localeCandidates)) return $supportedLocale;
}
}
// If there is only one candidate (or if we were
// unable to disambiguate) then return the unique
// (first) candidate found.
return array_shift($localeCandidates);
}
/**
* Translate the ISO 2-letter language string (ISO639-1) into ISO639-3.
* @param $iso1 string
* @return string the translated string or null if we
* don't know about the given language.
*/
static function getIso3FromIso1($iso1) {
assert(strlen($iso1) == 2);
$locales =& AppLocale::_getAllLocalesCacheContent();
foreach($locales as $locale => $localeData) {
if (substr($locale, 0, 2) == $iso1) {
assert(isset($localeData['iso639-3']));
return $localeData['iso639-3'];
}
}
return null;
}
/**
* Translate the ISO639-3 into ISO639-1.
* @param $iso3 string
* @return string the translated string or null if we
* don't know about the given language.
*/
static function getIso1FromIso3($iso3) {
assert(strlen($iso3) == 3);
$locales =& AppLocale::_getAllLocalesCacheContent();
foreach($locales as $locale => $localeData) {
assert(isset($localeData['iso639-3']));
if ($localeData['iso639-3'] == $iso3) {
return substr($locale, 0, 2);
}
}
return null;
}
/**
* Translate the PKP locale identifier into an
* ISO639-3 compatible 3-letter string.
* @param $locale string
* @return string
*/
static function getIso3FromLocale($locale) {
assert(strlen($locale) >= 5);
$iso1 = substr($locale, 0, 2);
return AppLocale::getIso3FromIso1($iso1);
}
/**
* Translate the PKP locale identifier into an
* ISO639-1 compatible 2-letter string.
* @param $locale string
* @return string
*/
static function getIso1FromLocale($locale) {
assert(strlen($locale) >= 5);
return substr($locale, 0, 2);
}
/**
* Translate an ISO639-3 compatible 3-letter string
* into the PKP locale identifier.
*
* This can be ambiguous if several locales are defined
* for the same language. In this case we'll use the
* primary locale to disambiguate.
*
* If that still doesn't determine a unique locale then
* we'll choose the first locale found.
*
* @param $iso3 string
* @return string
*/
static function getLocaleFromIso3($iso3) {
assert(strlen($iso3) == 3);
$primaryLocale = AppLocale::getPrimaryLocale();
$localeCandidates = array();
$locales =& AppLocale::_getAllLocalesCacheContent();
foreach($locales as $locale => $localeData) {
assert(isset($localeData['iso639-3']));
if ($localeData['iso639-3'] == $iso3) {
if ($locale == $primaryLocale) {
// In case of ambiguity the primary locale
// overrides all other options so we're done.
return $primaryLocale;
}
$localeCandidates[] = $locale;
}
}
// Return null if we found no candidate locale.
if (empty($localeCandidates)) return null;
if (count($localeCandidates) > 1) {
// Check whether one of the candidate locales
// is a supported locale. If so choose the first
// supported locale.
$supportedLocales = AppLocale::getSupportedLocales();
foreach($supportedLocales as $supportedLocale => $localeName) {
if (in_array($supportedLocale, $localeCandidates)) return $supportedLocale;
}
}
// If there is only one candidate (or if we were
// unable to disambiguate) then return the unique
// (first) candidate found.
return array_shift($localeCandidates);
}
//
// Private helper methods.
//
/**
* Retrieves locale data from the locales cache.
* @return array
*/
static function &_getAllLocalesCacheContent() {
static $contents = false;
if ($contents === false) {
$allLocalesCache =& AppLocale::_getAllLocalesCache();
$contents = $allLocalesCache->getContents();
}
return $contents;
}
/**
* Get the cache object for the current list of all locales.
* @return FileCache
*/
static function &_getAllLocalesCache() {
$cache =& Registry::get('allLocalesCache', true, null);
if ($cache === null) {
$cacheManager = CacheManager::getManager();
$cache = $cacheManager->getFileCache(
'locale', 'list',
array('AppLocale', '_allLocalesCacheMiss')
);
// Check to see if the data is outdated
$cacheTime = $cache->getCacheTime();
if ($cacheTime !== null && $cacheTime < filemtime(LOCALE_REGISTRY_FILE)) {
$cache->flush();
}
}
return $cache;
}
/**
* Create a cache file with locale data.
* @param $cache CacheManager
* @param $id the cache id (not used here, required by the cache manager)
*/
static function _allLocalesCacheMiss($cache, $id) {
$allLocales =& Registry::get('allLocales', true, null);
if ($allLocales === null) {
// Add a locale load to the debug notes.
$notes =& Registry::get('system.debug.notes');
$notes[] = array('debug.notes.localeListLoad', array('localeList' => LOCALE_REGISTRY_FILE));
// Reload locale registry file
$allLocales = AppLocale::loadLocaleList(LOCALE_REGISTRY_FILE);
asort($allLocales);
$cache->setEntireCache($allLocales);
}
return null;
}
/**
* Get the sites time zone.
* @return string Time zone
*/
static function getTimeZone() {
$timeZone = null;
// Load the time zone from the configuration file
if ($timeZoneConfig = Config::getVar('general', 'time_zone')) {
$timeZoneDAO = DAORegistry::getDAO('TimeZoneDAO');
$timeZoneList = $timeZoneDAO->getTimeZones();
foreach ($timeZoneList as $timeZoneKey => $timeZoneName) {
if (in_array($timeZoneConfig, array($timeZoneKey, $timeZoneName))) {
$timeZone = $timeZoneKey;
break;
}
}
}
// Fall back to the time zone set in php.ini
if (empty($timeZone)) $timeZone = ini_get('date.timezone');
// Fall back to UTC
if (empty($timeZone)) $timeZone = 'UTC';
return $timeZone;
}
}
/**
* Wrapper around PKPLocale::translate().
*
* Enables us to work with translated strings everywhere without
* introducing a lot of duplicate code and without getting
* blisters on our fingers.
*
* This is similar to WordPress' solution for translation, see
* <http://codex.wordpress.org/Translating_WordPress>.
*
* @see PKPLocale::translate()
*
* @param $key string
* @param $params array named substitution parameters
* @param $locale string the locale to use
* @return string
*/
function __($key, $params = array(), $locale = null, $missingKeyHandler = array('PKPLocale', 'addOctothorpes')) {
return AppLocale::translate($key, $params, $locale, $missingKeyHandler);
}
|