mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API CHANGE Using Zend_Translate with YAML translation files, replacing the $lang global and PHP files in the /lang folders.
This commit is contained in:
parent
0a0be63ee2
commit
bd23a07bba
@ -178,7 +178,7 @@ class SS_ConfigManifest {
|
||||
|
||||
// We use Symfony Yaml since it's the most complete. It still doesn't handle all of YAML, but it's better than
|
||||
// nothing.
|
||||
require_once 'thirdparty/symfony-yaml/lib/sfYamlParser.php';
|
||||
if(!class_exists('sfYamlParser', false)) require_once 'thirdparty/symfony-yaml/lib/sfYamlParser.php';
|
||||
$parser = new sfYamlParser();
|
||||
|
||||
// The base header
|
||||
|
347
i18n/i18n.php
347
i18n/i18n.php
@ -1,4 +1,7 @@
|
||||
<?php
|
||||
require_once 'Zend/Translate.php';
|
||||
require_once 'i18nRailsYamlAdapter.php';
|
||||
|
||||
/**
|
||||
* Base-class for storage and retrieval of translated entities.
|
||||
*
|
||||
@ -84,6 +87,11 @@ class i18n extends Object implements TemplateGlobalProvider {
|
||||
*/
|
||||
protected static $time_format;
|
||||
|
||||
/**
|
||||
* @var array Array of priority keys to instances of Zend_Translate, mapped by name.
|
||||
*/
|
||||
protected static $translators;
|
||||
|
||||
/**
|
||||
* Use javascript i18n through the ss.i18n class (enabled by default).
|
||||
* If set to TRUE, includes javascript requirements for the base library
|
||||
@ -1452,24 +1460,98 @@ class i18n extends Object implements TemplateGlobalProvider {
|
||||
* @return string The translated string, according to the currently set locale {@link i18n::set_locale()}
|
||||
*/
|
||||
static function _t($entity, $string = "", $priority = 40, $context = "") {
|
||||
global $lang;
|
||||
foreach(self::get_translators() as $priority => $translators) {
|
||||
foreach($translators as $name => $translator) {
|
||||
$adapter = $translator->getAdapter();
|
||||
|
||||
// get current locale (either default or user preference)
|
||||
$locale = i18n::get_locale();
|
||||
|
||||
// parse $entity into its parts
|
||||
$entityParts = explode('.',$entity);
|
||||
$realEntity = array_pop($entityParts);
|
||||
$class = implode('.',$entityParts);
|
||||
$adapter->setLocale($locale);
|
||||
|
||||
// if language table isn't loaded for this locale, get it for each of the modules
|
||||
if(!isset($lang[$locale])) i18n::include_by_locale($locale);
|
||||
if(!$adapter->isAvailable($locale)) i18n::include_by_locale($locale);
|
||||
|
||||
// fallback to the passed $string if no translation is present
|
||||
$transEntity = isset($lang[$locale][$class][$realEntity]) ? $lang[$locale][$class][$realEntity] : $string;
|
||||
$translation = $adapter->translate($entity, $locale);
|
||||
|
||||
// entities can be stored in both array and literal values in the language tables
|
||||
return (is_array($transEntity) ? $transEntity[0] : $transEntity);
|
||||
// Return translation only if we found a match thats not the entity itself (Zend fallback)
|
||||
if($translation && $translation != $entity) return $translation;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to default string argument
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Array of priority keys to instances of Zend_Translate, mapped by name.
|
||||
*/
|
||||
static function get_translators() {
|
||||
if(!self::$translators) {
|
||||
Zend_Translate::setCache(
|
||||
SS_Cache::factory('i18n', 'Output', array('lifetime' => -1, 'automatic_serialization' => true))
|
||||
);
|
||||
|
||||
$defaultPriority = 10;
|
||||
self::$translators[$defaultPriority] = array(
|
||||
'core' => new Zend_Translate(array(
|
||||
'adapter' => 'i18nRailsYamlAdapter',
|
||||
'locale' => self::$default_locale,
|
||||
'disableNotices' => true,
|
||||
))
|
||||
);
|
||||
self::$translators[$defaultPriority-1] = array(
|
||||
'legacy' => new Zend_Translate(array(
|
||||
'adapter' => 'i18nSSLegacyAdapter',
|
||||
'locale' => self::$default_locale,
|
||||
'disableNotices' => true,
|
||||
))
|
||||
);
|
||||
|
||||
i18n::include_by_locale('en_US');
|
||||
}
|
||||
|
||||
return self::$translators;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String
|
||||
* @return Zend_Translate
|
||||
*/
|
||||
static function get_translator($name) {
|
||||
foreach(self::get_translators() as $priority => $translators) {
|
||||
if(isset($translators[$name])) return $translators[$name];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Zend_Translate Needs to implement {@link i18nTranslateAdapterInterface}
|
||||
* @param String If left blank will override the default translator.
|
||||
* @param Int
|
||||
*/
|
||||
static function register_translator($translator, $name, $priority = 10) {
|
||||
if (!is_int($priority)) throw new InvalidArgumentException("register_translator expects an int priority");
|
||||
|
||||
// Ensure it's not there. If it is, we're replacing it. It may exist in a different priority.
|
||||
self::unregister_translator($name);
|
||||
|
||||
// Add our new translator
|
||||
if(!isset(self::$translators[$priority])) self::$translators[$priority] = array();
|
||||
self::$translators[$priority][$name] = $translator;
|
||||
|
||||
// Resort array, ensuring highest priority comes first
|
||||
krsort(self::$translators);
|
||||
|
||||
i18n::include_by_locale('en_US');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String
|
||||
*/
|
||||
static function unregister_translator($name) {
|
||||
foreach (self::get_translators() as $priority => $translators) {
|
||||
if (isset($translators[$name])) unset(self::$translators[$priority][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1517,31 +1599,41 @@ class i18n extends Object implements TemplateGlobalProvider {
|
||||
* @return array
|
||||
*/
|
||||
static function get_existing_translations() {
|
||||
$locales = array();
|
||||
$localeWithTitles = array();
|
||||
|
||||
$baseDir = Director::baseFolder();
|
||||
$modules = scandir($baseDir);
|
||||
foreach(self::get_translators() as $priority => $translators) {
|
||||
foreach($translators as $name => $translator) {
|
||||
$adapter = $translator->getAdapter();
|
||||
// TODO Inspect themes
|
||||
$modules = SS_ClassLoader::instance()->getManifest()->getModules();
|
||||
foreach($modules as $module) {
|
||||
if($module[0] == '.') continue;
|
||||
if(!file_exists("{$module}/lang/")) continue;
|
||||
$adapter->addTranslation(array(
|
||||
'content' => "{$module}/lang/",
|
||||
'scan' => Zend_Translate_Adapter::LOCALE_FILENAME,
|
||||
// TODO Support custom translators with their own file extensions
|
||||
'ignore' => array(
|
||||
'.',
|
||||
'_manifest_exclude',
|
||||
'regex' => '/^.*\.(?!yml).*$/i'
|
||||
)
|
||||
));
|
||||
}
|
||||
$locales = $adapter->getList();
|
||||
foreach($locales as $locale) {
|
||||
// Normalize locale to include likely region tag.
|
||||
// TODO Replace with CLDR list of actually available languages/regions
|
||||
$locale = self::get_locale_from_lang($locale);
|
||||
|
||||
$moduleDir = $baseDir . DIRECTORY_SEPARATOR . $module;
|
||||
$langDir = $moduleDir . DIRECTORY_SEPARATOR . "lang";
|
||||
if(is_dir($moduleDir) && is_file($moduleDir . DIRECTORY_SEPARATOR . "_config.php") && is_dir($langDir)) {
|
||||
$moduleLocales = scandir($langDir);
|
||||
foreach($moduleLocales as $moduleLocale) {
|
||||
if(preg_match('/(.*)\.php$/',$moduleLocale, $matches)) {
|
||||
if(isset($matches[1]) && isset(self::$all_locales[$matches[1]])) {
|
||||
$locales[$matches[1]] = self::$all_locales[$matches[1]];
|
||||
}
|
||||
}
|
||||
$localeWithTitles[$locale] = (@self::$all_locales[$locale]) ? self::$all_locales[$locale] : $locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort by title (not locale)
|
||||
asort($locales);
|
||||
asort($localeWithTitles);
|
||||
|
||||
return $locales;
|
||||
return $localeWithTitles;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1729,8 +1821,6 @@ class i18n extends Object implements TemplateGlobalProvider {
|
||||
* @param string $locale Locale to be set. See http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html for a list of possible locales.
|
||||
*/
|
||||
static function set_locale($locale) {
|
||||
if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
|
||||
|
||||
if ($locale) self::$current_locale = $locale;
|
||||
}
|
||||
|
||||
@ -1767,77 +1857,78 @@ class i18n extends Object implements TemplateGlobalProvider {
|
||||
* @param String $locale
|
||||
*/
|
||||
static function set_default_locale($locale) {
|
||||
if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
|
||||
|
||||
self::$default_locale = $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a locale file determined by module name and locale
|
||||
*
|
||||
* @deprecated 3.0 Use Zend_Translate instead
|
||||
*
|
||||
* @param string $module Module that contains the locale file
|
||||
* @param string $locale Locale to be loaded
|
||||
*/
|
||||
static function include_locale_file($module, $locale) {
|
||||
if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
|
||||
Deprecation::notice('3.0', 'Use Zend_Translate instead.');
|
||||
|
||||
if (file_exists($file = Director::getAbsFile("$module/lang/$locale.php"))) include_once($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes all available language files for a certain defined locale
|
||||
* Includes all available language files for a certain defined locale.
|
||||
* If the locale is a fully qualified locale (e.g. "en_US" rather than "en"),
|
||||
* will load the base locale file as well (if available).
|
||||
*
|
||||
* @param string $locale All resources from any module in locale $locale will be loaded
|
||||
* @param boolean $load_plugins If true (default), load extra translations from registered plugins
|
||||
* @param boolean $force_load If true (not default), we force the inclusion. Generally this should be off
|
||||
* for performance, but enabling this is useful for interfaces like
|
||||
* CustomTranslationAdmin which need to load more than the usual locales,
|
||||
* and may need to reload them.
|
||||
*/
|
||||
static function include_by_locale($locale, $load_plugins = true, $force_load = false) {
|
||||
if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
|
||||
static function include_by_locale($locale) {
|
||||
$lang = i18n::get_lang_from_locale($locale);
|
||||
|
||||
global $lang;
|
||||
// Automatically include fallback language (if applicable)
|
||||
// TODO Also include custom Zend_Translate routing languages
|
||||
$selectedLocales = array_unique(array($lang, $locale));
|
||||
|
||||
$base = Director::baseFolder();
|
||||
$topLevel = scandir($base);
|
||||
// Loop in reverse order, meaning the translator with the highest priority goes first
|
||||
$translators = array_reverse(self::get_translators(), true);
|
||||
foreach($translators as $priority => $translators) {
|
||||
foreach($translators as $name => $translator) {
|
||||
$adapter = $translator->getAdapter();
|
||||
$modules = SS_ClassLoader::instance()->getManifest()->getModules();
|
||||
|
||||
foreach($topLevel as $module) {
|
||||
// $topLevel is the website root, some servers are configured not to allow excess website root's parent level
|
||||
// and we don't need to check website root's parent level and website root level for its lang folder, so
|
||||
// we skip these 2 levels checking.
|
||||
if($module[0] == '.') continue;
|
||||
|
||||
if (
|
||||
is_dir("$base/$module")
|
||||
&& file_exists("$base/$module/_config.php")
|
||||
&& file_exists($file = "$base/$module/lang/$locale.php")
|
||||
) {
|
||||
if ($force_load) include($file);
|
||||
else include_once($file);
|
||||
// Load translations from modules
|
||||
foreach($modules as $module) {
|
||||
foreach($selectedLocales as $selectedLocale) {
|
||||
$filename = $adapter->getFilenameForLocale($selectedLocale);
|
||||
$filepath = "{$module}/lang/" . $filename;
|
||||
if($filename && !file_exists($filepath)) continue;
|
||||
$adapter->addTranslation(
|
||||
array('content' => $filepath, 'locale' => $selectedLocale)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Load translations from themes
|
||||
$themesBase = $base . '/themes';
|
||||
|
||||
if(is_dir($themesBase) && SSViewer::current_theme()) {
|
||||
// TODO Replace with theme listing once implemented in TemplateManifest
|
||||
$themesBase = Director::baseFolder() . '/themes';
|
||||
if(is_dir($themesBase)) {
|
||||
foreach(scandir($themesBase) as $theme) {
|
||||
if(
|
||||
strpos($theme, SSViewer::current_theme()) === 0
|
||||
&& file_exists($file = "$themesBase/$theme/lang/$locale.php")
|
||||
&& file_exists("{$themesBase}/{$theme}/lang/")
|
||||
) {
|
||||
if ($force_load) include($file);
|
||||
else include_once($file);
|
||||
foreach($selectedLocales as $selectedLocale) {
|
||||
$filename = $adapter->getFilenameForLocale($selectedLocale);
|
||||
$filepath = "{$themesBase}/{$theme}/lang/" . $filename;
|
||||
if($filename && !file_exists($filepath)) continue;
|
||||
$adapter->addTranslation(
|
||||
array('content' => $filepath, 'locale' => $selectedLocale)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load any translations from registered plugins
|
||||
if ($load_plugins) self::plugins_load($locale);
|
||||
|
||||
// Make sure this is only done once. We don't want to attempt it hundreds of times for missing locals
|
||||
if(!isset($lang[$locale])) $lang[$locale] = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1850,112 +1941,17 @@ class i18n extends Object implements TemplateGlobalProvider {
|
||||
static function include_by_class($class) {
|
||||
$module = self::get_owner_module($class);
|
||||
|
||||
if(!$module) user_error("i18n::include_by_class: Class {$class} not found", E_USER_WARNING);
|
||||
$locale = self::get_locale();
|
||||
|
||||
if (file_exists($file = Director::getAbsFile("$module/lang/". self::get_locale() . '.php'))) {
|
||||
include($file);
|
||||
} else if (self::get_locale() != self::$default_locale) {
|
||||
$old = self::get_locale();
|
||||
self::set_locale(self::$default_locale);
|
||||
self::include_by_class($class);
|
||||
self::set_locale($old);
|
||||
|
||||
} else if(file_exists(Director::getAbsFile("$module/lang"))) {
|
||||
user_error("i18n::include_by_class: Locale file $file should exist", E_USER_WARNING);
|
||||
}
|
||||
|
||||
// If the language file wasn't included for this class, include an empty array to prevent
|
||||
// this method from being called again
|
||||
global $lang;
|
||||
if(!isset($lang[$locale][$class])) $lang[$locale][$class] = array();
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------//
|
||||
|
||||
/**
|
||||
* This variable holds translation plugins that are invoked on a call to _t. It is a two dimensional array,
|
||||
* priority the first dimension and name the second, mapping to the callback.
|
||||
* Translations from lower priority plugins are used first, and callback is a callback for call_user_func_array.
|
||||
*
|
||||
* Callback functions are passed one parameter:
|
||||
* - locale string
|
||||
* The callback function should return an array that can be merged with $lang[$locale], overriding values read
|
||||
* from the language file.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $plugins = array();
|
||||
|
||||
/**
|
||||
* Register a named translation plug-in function.
|
||||
* Plug-ins are assumed to be registered before any call to _t. If registered after a call to _t
|
||||
* for a given local, it will not be called.
|
||||
* @static
|
||||
* @throws Exception
|
||||
* @param $name String A unique name for the translation plug-in. If the plug-in is already registered,
|
||||
* it is replaced, including if its a different priority.
|
||||
* @param $callback A callback function as given to call_user_func_array.
|
||||
* @param int $priority An integer priority, default 10.
|
||||
* @return void
|
||||
*/
|
||||
static function register_plugin($name, $callback, $priority = 10) {
|
||||
// Validate
|
||||
if (!is_int($priority)) throw new Exception("register_plugin expects an int priority");
|
||||
|
||||
// Ensure it's not there. If it is, we're replacing it. It may exist in a different priority.
|
||||
self::unregister_plugin($name);
|
||||
|
||||
// Add it.
|
||||
self::$plugins[$priority][$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a plugin by name.
|
||||
* @static
|
||||
* @param $name String Name of previously registered plugin
|
||||
* @return Boolean Returns true if remove, false if not.
|
||||
*/
|
||||
static function unregister_plugin($name) {
|
||||
foreach (self::$plugins as $priority => $plugins) {
|
||||
if (isset($plugins[$name])) unset(self::$plugins[$priority][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load any translations from registered plugins. Merges them directly into $lang.
|
||||
* @static
|
||||
* @param $local
|
||||
* @param $value
|
||||
* @return void
|
||||
*/
|
||||
static function plugins_load($locale) {
|
||||
// sort the plugins by lowest priority (highest value) first, as each one replaces translations of the provider
|
||||
// before it.
|
||||
krsort(self::$plugins);
|
||||
foreach (self::$plugins as $priority => $plugins) {
|
||||
foreach ($plugins as $name => $callback) {
|
||||
self::merge_locale_data($locale, call_user_func_array($callback, array($locale)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge an extra of language translations into $lang[$locale]. We'd use array_merge_recursive, except
|
||||
* it doesn't work for translations that specify priorities and comments, because they are indexed by number.
|
||||
* @static
|
||||
* @param $locale String The locale we are merging into
|
||||
* @param $extra Array An array of [locale][class][entity]=> translation, keyed on entity, that are to be
|
||||
* merged for this locale.
|
||||
* @return void
|
||||
*/
|
||||
static function merge_locale_data($locale, $extra) {
|
||||
global $lang;
|
||||
if (!$extra || count($extra) == 0 || !isset($extra[$locale])) return;
|
||||
foreach ($extra[$locale] as $class => $entities) {
|
||||
foreach ($entities as $entity => $translation) {
|
||||
$lang[$locale][$class][$entity] = $translation;
|
||||
$translators = array_reverse(self::get_translators(), true);
|
||||
foreach($translators as $priority => $translators) {
|
||||
foreach($translators as $name => $translator) {
|
||||
$adapter = $translator->getAdapter();
|
||||
$filename = $adapter->getFilenameForLocale(self::get_locale());
|
||||
$filepath = "{$module}/lang/" . $filename;
|
||||
if($filename && !file_exists($filepath)) continue;
|
||||
$adapter->addTranslation(array(
|
||||
'content' => $filepath,
|
||||
'locale' => self::get_locale()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1966,6 +1962,5 @@ class i18n extends Object implements TemplateGlobalProvider {
|
||||
'get_locale',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
19
i18n/i18nRailsYamlAdapter.php
Normal file
19
i18n/i18nRailsYamlAdapter.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
require_once 'Zend/Translate.php';
|
||||
require_once 'zend_translate_railsyaml/library/Translate/Adapter/RailsYaml.php';
|
||||
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
class i18nRailsYamlAdapter extends Translate_Adapter_RailsYaml implements i18nTranslateAdapterInterface {
|
||||
|
||||
/**
|
||||
* @param String
|
||||
* @return String
|
||||
*/
|
||||
public function getFilenameForLocale($locale) {
|
||||
return "$locale.yml";
|
||||
}
|
||||
}
|
87
i18n/i18nSSLegacyAdapter.php
Normal file
87
i18n/i18nSSLegacyAdapter.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
require_once 'Zend/Locale.php';
|
||||
require_once 'Zend/Translate/Adapter.php';
|
||||
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
class i18nSSLegacyAdapter extends Zend_Translate_Adapter implements i18nTranslateAdapterInterface {
|
||||
|
||||
/**
|
||||
* Generates the adapter
|
||||
*
|
||||
* @param array|Zend_Config $options Translation content
|
||||
*/
|
||||
public function __construct($options = array()) {
|
||||
$this->_options['keyDelimiter'] = ".";
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
protected function _loadTranslationData($filename, $locale, array $options = array()) {
|
||||
$options = array_merge($this->_options, $options);
|
||||
|
||||
if ($options['clear'] || !isset($this->_translate[$locale])) {
|
||||
$this->_translate[$locale] = array();
|
||||
}
|
||||
|
||||
$this->_filename = $filename;
|
||||
|
||||
// Ignore files with other extensions
|
||||
if(pathinfo($this->_filename, PATHINFO_EXTENSION) != 'php') return;
|
||||
|
||||
if (!is_readable($this->_filename)) {
|
||||
require_once 'Zend/Translate/Exception.php';
|
||||
throw new Zend_Translate_Exception('Error opening translation file \'' . $filename . '\'.');
|
||||
}
|
||||
|
||||
global $lang;
|
||||
if(!isset($lang['en_US'])) $lang['en_US'] = array();
|
||||
// TODO Diff locale array to avoid re-parsing all previous translations whenever a new module is included.
|
||||
require_once($this->_filename);
|
||||
|
||||
$flattened = array();
|
||||
if($lang[$locale]) {
|
||||
$iterator = new i18nSSLegacyAdapter_Iterator(new RecursiveArrayIterator($lang[$locale]));
|
||||
foreach($iterator as $k => $v) {
|
||||
$flattenedKey = implode($options['keyDelimiter'], array_filter($iterator->getKeyStack()));
|
||||
$flattened[$flattenedKey] = (is_array($v)) ? $v[0] : $v;
|
||||
}
|
||||
}
|
||||
|
||||
return array($locale => $flattened);
|
||||
}
|
||||
|
||||
public function toString() {
|
||||
return "i18nSSLegacy";
|
||||
}
|
||||
|
||||
function getFilenameForLocale($locale) {
|
||||
return "{$locale}.php";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class i18nSSLegacyAdapter_Iterator extends RecursiveIteratorIterator {
|
||||
|
||||
protected $keyStack = array();
|
||||
|
||||
public function callGetChildren() {
|
||||
$this->keyStack[] = parent::key();
|
||||
return parent::callGetChildren();
|
||||
}
|
||||
|
||||
public function endChildren() {
|
||||
array_pop($this->keyStack);
|
||||
parent::endChildren();
|
||||
}
|
||||
|
||||
public function key() {
|
||||
return json_encode($this->getKeyStack());
|
||||
}
|
||||
|
||||
public function getKeyStack() {
|
||||
return array_merge($this->keyStack, array(parent::key()));
|
||||
}
|
||||
}
|
23
i18n/i18nTranslateAdapterInterface.php
Normal file
23
i18n/i18nTranslateAdapterInterface.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
/**
|
||||
* Makes the {@link Zend_Translate_Adapter} base class aware of file naming conventions within SilverStripe.
|
||||
* Needs to be implemented by all translators used through {@link i18n::register_translator()}.
|
||||
*
|
||||
* A bit of context: Zend is file extension agnostic by default, and simply uses the filenames to detect locales
|
||||
* with the 'scan' option, passing all files to the used adapter. We support multiple formats in the same /lang/
|
||||
* folder, so need to be more selective about including files to avoid e.g. a YAML adapter trying to parse a PHP file.
|
||||
*
|
||||
* @see http://framework.zend.com/manual/en/zend.translate.additional.html#zend.translate.additional.combination
|
||||
*/
|
||||
interface i18nTranslateAdapterInterface {
|
||||
/**
|
||||
* @param String
|
||||
* @return String
|
||||
*/
|
||||
public function getFilenameForLocale($locale);
|
||||
}
|
@ -205,11 +205,6 @@ he-IL:
|
||||
LOSTPASSWORDHEADER: "איבדת סיסמא"
|
||||
NOTEPAGESECURED: "עמוד זה אינו מאובטח. הכנס את הפרטים שלך להלן ונשלח אליך מייד."
|
||||
NOTERESETPASSWORD: "הכנס את כתובת הדואר האלקטרוני שלך ונשלח אליך קישור שבעזרתו תוכל לאפס את הסיסמא שלך"
|
||||
PASSWORDSENTHEADER: |
|
||||
קישור לאיפוס סיסמא נשלח ל '%s'
|
||||
PASSWORDSENTTEXT: |
|
||||
תודה רבה! קישור לאיפוס הסיסמא נשלח ל '%s'.
|
||||
SecurityAdmin:
|
||||
ADDMEMBER: "הוסף חבר"
|
||||
EDITPERMISSIONS: "ערוך הרשאות וכתובות IP לכל קבוצה"
|
||||
MENUTITLE: "אבטחה"
|
||||
|
4
tests/i18n/_fakewebroot/i18nothermodule/lang/de.yml
Normal file
4
tests/i18n/_fakewebroot/i18nothermodule/lang/de.yml
Normal file
@ -0,0 +1,4 @@
|
||||
de:
|
||||
i18nOtherModule:
|
||||
ENTITY: Other Module Entity (de)
|
||||
MAINTEMPLATE: Main Template Other Module (de)
|
13
tests/i18n/_fakewebroot/i18nothermodule/lang/de_DE.php
Normal file
13
tests/i18n/_fakewebroot/i18nothermodule/lang/de_DE.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
i18n::include_locale_file('i18nothermodule', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('de_DE', $lang) && is_array($lang['de_DE'])) {
|
||||
$lang['de_DE'] = array_merge($lang['en_US'], $lang['de_DE']);
|
||||
} else {
|
||||
$lang['de_DE'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['de_DE']['i18nOtherModule']['LEGACY'] = 'Legacy translation (de_DE)';
|
4
tests/i18n/_fakewebroot/i18nothermodule/lang/en.yml
Normal file
4
tests/i18n/_fakewebroot/i18nothermodule/lang/en.yml
Normal file
@ -0,0 +1,4 @@
|
||||
en:
|
||||
i18nOtherModule:
|
||||
ENTITY: Other Module Entity
|
||||
MAINTEMPLATE: Main Template Other Module
|
5
tests/i18n/_fakewebroot/i18nothermodule/lang/en_US.php
Normal file
5
tests/i18n/_fakewebroot/i18nothermodule/lang/en_US.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
global $lang;
|
||||
|
||||
$lang['en_US']['i18nOtherModule']['LEGACY'] = 'Legacy translation';
|
@ -0,0 +1,3 @@
|
||||
de:
|
||||
i18nTestModule:
|
||||
OTHERENTITY: Other Entity (de)
|
@ -0,0 +1,3 @@
|
||||
en:
|
||||
i18nTestModule:
|
||||
OTHERENTITY: Other Entity
|
15
tests/i18n/_fakewebroot/i18ntestmodule/lang/de.yml
Normal file
15
tests/i18n/_fakewebroot/i18ntestmodule/lang/de.yml
Normal file
@ -0,0 +1,15 @@
|
||||
de:
|
||||
NONAMESPACE: Include Entity without Namespace (de)
|
||||
SPRINTFNONAMESPACE: My replacement no namespace: %s (de)
|
||||
SPRINTFINCLUDENONAMESPACE: My include replacement no namespace: %s (de)
|
||||
LAYOUTTEMPLATENONAMESPACE: Layout Template no namespace (de)
|
||||
i18nTestModule:
|
||||
# Comment for entity
|
||||
ENTITY: Entity with "Double Quotes" (de)
|
||||
ADDITION: Addition (de)
|
||||
MAINTEMPLATE: Main Template (de)
|
||||
WITHNAMESPACE: Include Entity with Namespace (de)
|
||||
LAYOUTTEMPLATE: Layout Template (de)
|
||||
SPRINTFNAMESPACE: My replacement: %s (de)
|
||||
i18nTestModuleInclude.ss:
|
||||
SPRINTFINCLUDENAMESPACE: My include replacement: %s (de)
|
3
tests/i18n/_fakewebroot/i18ntestmodule/lang/de_AT.yml
Normal file
3
tests/i18n/_fakewebroot/i18ntestmodule/lang/de_AT.yml
Normal file
@ -0,0 +1,3 @@
|
||||
de_AT:
|
||||
i18nTestModule:
|
||||
ENTITY: Entity with "Double Quotes" (de_AT)
|
15
tests/i18n/_fakewebroot/i18ntestmodule/lang/en.yml
Normal file
15
tests/i18n/_fakewebroot/i18ntestmodule/lang/en.yml
Normal file
@ -0,0 +1,15 @@
|
||||
en:
|
||||
NONAMESPACE: Include Entity without Namespace
|
||||
SPRINTFNONAMESPACE: My replacement no namespace: %s
|
||||
SPRINTFINCLUDENONAMESPACE: My include replacement no namespace: %s
|
||||
LAYOUTTEMPLATENONAMESPACE: Layout Template no namespace
|
||||
i18nTestModule:
|
||||
# Comment for entity
|
||||
ENTITY: Entity with "Double Quotes"
|
||||
ADDITION: Addition
|
||||
MAINTEMPLATE: Main Template
|
||||
WITHNAMESPACE: Include Entity with Namespace
|
||||
LAYOUTTEMPLATE: Layout Template
|
||||
SPRINTFNAMESPACE: My replacement: %s
|
||||
i18nTestModuleInclude.ss:
|
||||
SPRINTFINCLUDENAMESPACE: My include replacement: %s
|
3
tests/i18n/_fakewebroot/i18ntestmodule/lang/fr.yml
Normal file
3
tests/i18n/_fakewebroot/i18ntestmodule/lang/fr.yml
Normal file
@ -0,0 +1,3 @@
|
||||
fr:
|
||||
i18nTestModule:
|
||||
ENTITY: Entity with "Double Quotes" (fr)
|
11
tests/i18n/_fakewebroot/themes/testtheme1/lang/de.yml
Normal file
11
tests/i18n/_fakewebroot/themes/testtheme1/lang/de.yml
Normal file
@ -0,0 +1,11 @@
|
||||
de:
|
||||
i18nTestTheme1:
|
||||
LAYOUTTEMPLATE: Theme1 Layout Template (de)
|
||||
SPRINTFNAMESPACE: Theme1 My replacement: %s (de)
|
||||
i18nTestTheme1Include:
|
||||
WITHNAMESPACE: Theme1 Include Entity with Namespace (de)
|
||||
SPRINTFINCLUDENAMESPACE: Theme1 My include replacement: %s (de)
|
||||
NONAMESPACE: Theme1 Include Entity without Namespace (de)
|
||||
SPRINTFINCLUDENONAMESPACE: Theme1 My include replacement no namespace: %s (de)
|
||||
LAYOUTTEMPLATENONAMESPACE: Theme1 Layout Template no namespace (de)
|
||||
SPRINTFNONAMESPACE: Theme1 My replacement no namespace: %s (de)
|
13
tests/i18n/_fakewebroot/themes/testtheme1/lang/de_DE.php
Normal file
13
tests/i18n/_fakewebroot/themes/testtheme1/lang/de_DE.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
i18n::include_locale_file('i18nothermodule', 'en_US');
|
||||
|
||||
global $lang;
|
||||
|
||||
if(array_key_exists('de_DE', $lang) && is_array($lang['de_DE'])) {
|
||||
$lang['de_DE'] = array_merge($lang['en_US'], $lang['de_DE']);
|
||||
} else {
|
||||
$lang['de_DE'] = $lang['en_US'];
|
||||
}
|
||||
|
||||
$lang['de_DE']['i18nOtherModule']['LEGACYTHEME'] = 'Legacy translation (de_DE)';
|
11
tests/i18n/_fakewebroot/themes/testtheme1/lang/en.yml
Normal file
11
tests/i18n/_fakewebroot/themes/testtheme1/lang/en.yml
Normal file
@ -0,0 +1,11 @@
|
||||
en:
|
||||
i18nTestTheme1:
|
||||
LAYOUTTEMPLATE: Theme1 Layout Template
|
||||
SPRINTFNAMESPACE: Theme1 My replacement: %s
|
||||
i18nTestTheme1Include:
|
||||
WITHNAMESPACE: Theme1 Include Entity with Namespace
|
||||
SPRINTFINCLUDENAMESPACE: Theme1 My include replacement: %s
|
||||
NONAMESPACE: Theme1 Include Entity without Namespace
|
||||
SPRINTFINCLUDENONAMESPACE: Theme1 My include replacement no namespace: %s
|
||||
LAYOUTTEMPLATENONAMESPACE: Theme1 Layout Template no namespace
|
||||
SPRINTFNONAMESPACE: Theme1 My replacement no namespace: %s
|
5
tests/i18n/_fakewebroot/themes/testtheme1/lang/en_US.php
Normal file
5
tests/i18n/_fakewebroot/themes/testtheme1/lang/en_US.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
global $lang;
|
||||
|
||||
$lang['en_US']['i18nOtherModule']['LEGACYTHEME'] = 'Legacy translation';
|
3
tests/i18n/_fakewebroot/themes/testtheme2/lang/de.yml
Normal file
3
tests/i18n/_fakewebroot/themes/testtheme2/lang/de.yml
Normal file
@ -0,0 +1,3 @@
|
||||
de:
|
||||
i18nTestTheme2:
|
||||
MAINTEMPLATE: Theme2 Main Template (de)
|
3
tests/i18n/_fakewebroot/themes/testtheme2/lang/en.yml
Normal file
3
tests/i18n/_fakewebroot/themes/testtheme2/lang/en.yml
Normal file
@ -0,0 +1,3 @@
|
||||
en:
|
||||
i18nTestTheme2:
|
||||
MAINTEMPLATE: Theme2 Main Template
|
90
tests/i18n/i18nSSLegacyAdapterTest.php
Normal file
90
tests/i18n/i18nSSLegacyAdapterTest.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
class i18nSSLegacyAdapterTest extends SapphireTest {
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->alternateBasePath = $this->getCurrentAbsolutePath() . "/_fakewebroot";
|
||||
$this->alternateBaseSavePath = TEMP_FOLDER . '/i18nTextCollectorTest_webroot';
|
||||
FileSystem::makeFolder($this->alternateBaseSavePath);
|
||||
Director::setBaseFolder($this->alternateBasePath);
|
||||
|
||||
// Push a template loader running from the fake webroot onto the stack.
|
||||
$templateManifest = new SS_TemplateManifest($this->alternateBasePath, false, true);
|
||||
$templateManifest->regenerate(false);
|
||||
SS_TemplateLoader::instance()->pushManifest($templateManifest);
|
||||
$this->_oldTheme = SSViewer::current_theme();
|
||||
SSViewer::set_theme('testtheme1');
|
||||
|
||||
$classManifest = new SS_ClassManifest($this->alternateBasePath, true, true, false);
|
||||
SS_ClassLoader::instance()->pushManifest($classManifest);
|
||||
|
||||
$this->originalLocale = i18n::get_locale();
|
||||
|
||||
// Override default adapter to avoid cached translations between tests.
|
||||
// Emulates behaviour in i18n::get_translators()
|
||||
$this->origAdapter = i18n::get_translator('core');
|
||||
$adapter = new Zend_Translate(array(
|
||||
'adapter' => 'i18nRailsYamlAdapter',
|
||||
'locale' => i18n::default_locale(),
|
||||
'disableNotices' => true,
|
||||
));
|
||||
i18n::register_translator($adapter, 'core');
|
||||
$adapter->removeCache();
|
||||
i18n::include_by_locale('en');
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
SS_TemplateLoader::instance()->popManifest();
|
||||
SS_ClassLoader::instance()->popManifest();
|
||||
i18n::set_locale($this->originalLocale);
|
||||
Director::setBaseFolder(null);
|
||||
SSViewer::set_theme($this->_oldTheme);
|
||||
i18n::register_translator($this->origAdapter, 'core');
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
function testTranslate() {
|
||||
i18n::set_locale('en_US');
|
||||
$this->assertEquals(
|
||||
'Legacy translation',
|
||||
// defined in i18nothermodule/lang/en_US.php
|
||||
i18n::_t('i18nOtherModule.LEGACY'),
|
||||
'Finds original strings in PHP module files'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Legacy translation',
|
||||
// defined in themes/testtheme1/lang/en_US.php
|
||||
i18n::_t('i18nOtherModule.LEGACYTHEME'),
|
||||
'Finds original strings in theme files'
|
||||
);
|
||||
i18n::set_locale('de_DE');
|
||||
$this->assertEquals(
|
||||
'Legacy translation (de_DE)',
|
||||
// defined in i18nothermodule/lang/de_DE.php
|
||||
i18n::_t('i18nOtherModule.LEGACY'),
|
||||
'Finds translations in PHP module files'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Legacy translation (de_DE)',
|
||||
// defined in themes/testtheme1/lang/de_DE.php
|
||||
i18n::_t('i18nOtherModule.LEGACYTHEME'),
|
||||
'Finds original strings in theme files'
|
||||
);
|
||||
// TODO Implement likely subtags solution
|
||||
// i18n::set_locale('de');
|
||||
// $this->assertEquals(
|
||||
// 'Legacy translation (de_DE)',
|
||||
// // defined in i18nothermodule/lang/de_DE.php
|
||||
// i18n::_t('i18nOtherModule.LEGACY'),
|
||||
// 'Finds translations in PHP module files if only language locale is set'
|
||||
// );
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once 'Zend/Translate.php';
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
@ -29,18 +31,36 @@ class i18nTest extends SapphireTest {
|
||||
$this->alternateBasePath = $this->getCurrentAbsolutePath() . "/_fakewebroot";
|
||||
$this->alternateBaseSavePath = TEMP_FOLDER . '/i18nTextCollectorTest_webroot';
|
||||
FileSystem::makeFolder($this->alternateBaseSavePath);
|
||||
Director::setBaseFolder($this->alternateBasePath);
|
||||
|
||||
// Push a template loader running from the fake webroot onto the stack.
|
||||
$manifest = new SS_TemplateManifest($this->alternateBasePath, false, true);
|
||||
$manifest->regenerate(false);
|
||||
SS_TemplateLoader::instance()->pushManifest($manifest);
|
||||
$templateManifest = new SS_TemplateManifest($this->alternateBasePath, false, true);
|
||||
$templateManifest->regenerate(false);
|
||||
SS_TemplateLoader::instance()->pushManifest($templateManifest);
|
||||
$this->_oldTheme = SSViewer::current_theme();
|
||||
SSViewer::set_theme('testtheme1');
|
||||
|
||||
$this->originalLocale = i18n::get_locale();
|
||||
|
||||
// Override default adapter to avoid cached translations between tests.
|
||||
// Emulates behaviour in i18n::get_translators()
|
||||
$this->origAdapter = i18n::get_translator('core');
|
||||
$adapter = new Zend_Translate(array(
|
||||
'adapter' => 'i18nRailsYamlAdapter',
|
||||
'locale' => i18n::default_locale(),
|
||||
'disableNotices' => true,
|
||||
));
|
||||
i18n::register_translator($adapter, 'core');
|
||||
$adapter->removeCache();
|
||||
i18n::include_by_locale('en');
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
SS_TemplateLoader::instance()->popManifest();
|
||||
i18n::set_locale($this->originalLocale);
|
||||
Director::setBaseFolder(null);
|
||||
SSViewer::set_theme($this->_oldTheme);
|
||||
i18n::register_translator($this->origAdapter, 'core');
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
@ -78,24 +98,30 @@ class i18nTest extends SapphireTest {
|
||||
function testGetExistingTranslations() {
|
||||
$translations = i18n::get_existing_translations();
|
||||
$this->assertTrue(isset($translations['en_US']), 'Checking for en_US translation');
|
||||
$this->assertEquals($translations['en_US'], 'English (United States)');
|
||||
$this->assertTrue(isset($translations['de_DE']), 'Checking for de_DE translation');
|
||||
}
|
||||
|
||||
function testDataObjectFieldLabels() {
|
||||
global $lang;
|
||||
$oldLocale = i18n::get_locale();
|
||||
i18n::set_locale('de_DE');
|
||||
$obj = new i18nTest_DataObject();
|
||||
|
||||
$lang['en_US']['i18nTest_DataObject']['MyProperty'] = 'MyProperty';
|
||||
$lang['de_DE']['i18nTest_DataObject']['MyProperty'] = 'Mein Attribut';
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTest_DataObject.MyProperty' => 'MyProperty'
|
||||
), 'en_US');
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTest_DataObject.MyProperty' => 'Mein Attribut'
|
||||
), 'de_DE');
|
||||
|
||||
$this->assertEquals(
|
||||
$obj->fieldLabel('MyProperty'),
|
||||
'Mein Attribut'
|
||||
);
|
||||
|
||||
$lang['en_US']['i18nTest_DataObject']['MyUntranslatedProperty'] = 'MyUntranslatedProperty';
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTest_DataObject.MyUntranslatedProperty' => 'Mein Attribut'
|
||||
), 'en_US');
|
||||
$this->assertEquals(
|
||||
$obj->fieldLabel('MyUntranslatedProperty'),
|
||||
'My Untranslated Property'
|
||||
@ -105,12 +131,16 @@ class i18nTest extends SapphireTest {
|
||||
}
|
||||
|
||||
function testProvideI18nEntities() {
|
||||
global $lang;
|
||||
$oldLocale = i18n::get_locale();
|
||||
$lang['en_US']['i18nTest_Object']['my_translatable_property'] = 'Untranslated';
|
||||
$lang['de_DE']['i18nTest_Object']['my_translatable_property'] = 'Übersetzt';
|
||||
|
||||
i18n::set_locale('en_US');
|
||||
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTest_Object.MyProperty' => 'Untranslated'
|
||||
), 'en_US');
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTest_Object.my_translatable_property' => 'Übersetzt'
|
||||
), 'de_DE');
|
||||
|
||||
$this->assertEquals(
|
||||
i18nTest_Object::$my_translatable_property,
|
||||
'Untranslated'
|
||||
@ -136,19 +166,21 @@ class i18nTest extends SapphireTest {
|
||||
}
|
||||
|
||||
function testTemplateTranslation() {
|
||||
global $lang;
|
||||
$oldLocale = i18n::get_locale();
|
||||
|
||||
i18n::set_locale('en_US');
|
||||
$lang['en_US']['i18nTestModule']['MAINTEMPLATE'] = 'Main Template';
|
||||
$lang['en_US']['i18nTestModule.ss']['SPRINTFNONAMESPACE'] = 'My replacement no namespace: %s';
|
||||
$lang['en_US']['i18nTestModule']['LAYOUTTEMPLATE'] = 'Layout Template';
|
||||
$lang['en_US']['i18nTestModule.ss']['LAYOUTTEMPLATENONAMESPACE'] = 'Layout Template no namespace';
|
||||
$lang['en_US']['i18nTestModule']['SPRINTFNAMESPACE'] = 'My replacement: %s';
|
||||
$lang['en_US']['i18nTestModule']['WITHNAMESPACE'] = 'Include Entity with Namespace';
|
||||
$lang['en_US']['i18nTestModuleInclude.ss']['NONAMESPACE'] = 'Include Entity without Namespace';
|
||||
$lang['en_US']['i18nTestModuleInclude.ss']['SPRINTFINCLUDENAMESPACE'] = 'My include replacement: %s';
|
||||
$lang['en_US']['i18nTestModuleInclude.ss']['SPRINTFINCLUDENONAMESPACE'] = 'My include replacement no namespace: %s';
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTestModule.MAINTEMPLATE' => 'Main Template',
|
||||
'i18nTestModule.ss.SPRINTFNONAMESPACE' => 'My replacement no namespace: %s',
|
||||
'i18nTestModule.LAYOUTTEMPLATE' => 'Layout Template',
|
||||
'i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE' => 'Layout Template no namespace',
|
||||
'i18nTestModule.SPRINTFNAMESPACE' => 'My replacement: %s',
|
||||
'i18nTestModule.WITHNAMESPACE' => 'Include Entity with Namespace',
|
||||
'i18nTestModuleInclude.ss.NONAMESPACE' => 'Include Entity without Namespace',
|
||||
'i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE' => 'My include replacement: %s',
|
||||
'i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE' => 'My include replacement no namespace: %s'
|
||||
), 'en_US');
|
||||
|
||||
$viewer = new SSViewer('i18nTestModule');
|
||||
$parsedHtml = $viewer->process(new ArrayData(array('TestProperty' => 'TestPropertyValue')));
|
||||
$this->assertContains(
|
||||
@ -161,15 +193,18 @@ class i18nTest extends SapphireTest {
|
||||
);
|
||||
|
||||
i18n::set_locale('de_DE');
|
||||
$lang['de_DE']['i18nTestModule']['MAINTEMPLATE'] = 'TRANS Main Template';
|
||||
$lang['de_DE']['i18nTestModule.ss']['SPRINTFNONAMESPACE'] = 'TRANS My replacement no namespace: %s';
|
||||
$lang['de_DE']['i18nTestModule']['LAYOUTTEMPLATE'] = 'TRANS Layout Template';
|
||||
$lang['de_DE']['i18nTestModule.ss']['LAYOUTTEMPLATENONAMESPACE'] = 'TRANS Layout Template no namespace';
|
||||
$lang['de_DE']['i18nTestModule']['SPRINTFNAMESPACE'] = 'TRANS My replacement: %s';
|
||||
$lang['de_DE']['i18nTestModule']['WITHNAMESPACE'] = 'TRANS Include Entity with Namespace';
|
||||
$lang['de_DE']['i18nTestModuleInclude.ss']['NONAMESPACE'] = 'TRANS Include Entity without Namespace';
|
||||
$lang['de_DE']['i18nTestModuleInclude.ss']['SPRINTFINCLUDENAMESPACE'] = 'TRANS My include replacement: %s';
|
||||
$lang['de_DE']['i18nTestModuleInclude.ss']['SPRINTFINCLUDENONAMESPACE'] = 'TRANS My include replacement no namespace: %s';
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTestModule.MAINTEMPLATE' => 'TRANS Main Template',
|
||||
'i18nTestModule.ss.SPRINTFNONAMESPACE' => 'TRANS My replacement no namespace: %s',
|
||||
'i18nTestModule.LAYOUTTEMPLATE' => 'TRANS Layout Template',
|
||||
'i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE' => 'TRANS Layout Template no namespace',
|
||||
'i18nTestModule.SPRINTFNAMESPACE' => 'TRANS My replacement: %s',
|
||||
'i18nTestModule.WITHNAMESPACE' => 'TRANS Include Entity with Namespace',
|
||||
'i18nTestModuleInclude.ss.NONAMESPACE' => 'TRANS Include Entity without Namespace',
|
||||
'i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE' => 'TRANS My include replacement: %s',
|
||||
'i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE' => 'TRANS My include replacement no namespace: %s'
|
||||
), 'de_DE');
|
||||
|
||||
$viewer = new SSViewer('i18nTestModule');
|
||||
$parsedHtml = $viewer->process(new ArrayData(array('TestProperty' => 'TestPropertyValue')));
|
||||
$this->assertContains(
|
||||
@ -214,29 +249,6 @@ class i18nTest extends SapphireTest {
|
||||
$this->assertEquals('xy_XY', i18n::get_locale_from_lang('xy'));
|
||||
}
|
||||
|
||||
function testRegisteredPlugin() {
|
||||
global $lang;
|
||||
|
||||
// save lang state, if we don't do this we may break other tests
|
||||
$oldLang = $lang;
|
||||
|
||||
$lang = array(); // clear translations
|
||||
i18n::register_plugin("testPlugin", array("i18nTest", "translationTestPlugin"));
|
||||
|
||||
// We have to simulate what include_by_locale() does, including loading translation provider data.
|
||||
$lang['en_US']["i18nTestProvider"]["foo"] = "bar_en";
|
||||
$lang['de_DE']["i18nTestProvider"]["foo"] = "bar_de";
|
||||
i18n::plugins_load('en_US');
|
||||
|
||||
i18n::set_locale('en_US');
|
||||
$this->assertEquals(_t("i18nTestProvider.foo"), "baz_en");
|
||||
i18n::set_locale('de_DE');
|
||||
$this->assertEquals(_t("i18nTestProvider.foo"), "bar_de");
|
||||
i18n::unregister_plugin("testTranslator");
|
||||
|
||||
$lang = $oldLang;
|
||||
}
|
||||
|
||||
function testValidateLocale() {
|
||||
$this->assertTrue(i18n::validate_locale('en_US'), 'Known locale in underscore format is valid');
|
||||
$this->assertTrue(i18n::validate_locale('en-US'), 'Known locale in dash format is valid');
|
||||
@ -245,11 +257,159 @@ class i18nTest extends SapphireTest {
|
||||
$this->assertFalse(i18n::validate_locale(''), 'Empty string is not valid');
|
||||
}
|
||||
|
||||
static function translationTestPlugin($locale) {
|
||||
$result = array();
|
||||
$result["en_US"]["i18nTestProvider"]["foo"] = "baz_en";
|
||||
return $result;
|
||||
function testTranslate() {
|
||||
$oldLocale = i18n::get_locale();
|
||||
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTestModule.ENTITY' => 'Entity with "Double Quotes"',
|
||||
), 'en_US');
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTestModule.ENTITY' => 'Entity with "Double Quotes" (de)',
|
||||
'i18nTestModule.ADDITION' => 'Addition (de)',
|
||||
), 'de');
|
||||
i18n::get_translator('core')->getAdapter()->addTranslation(array(
|
||||
'i18nTestModule.ENTITY' => 'Entity with "Double Quotes" (de_AT)',
|
||||
), 'de_AT');
|
||||
|
||||
|
||||
$this->assertEquals(i18n::_t('i18nTestModule.ENTITY'), 'Entity with "Double Quotes"',
|
||||
'Returns translation in default language'
|
||||
);
|
||||
|
||||
i18n::set_locale('de');
|
||||
$this->assertEquals(i18n::_t('i18nTestModule.ENTITY'), 'Entity with "Double Quotes" (de)',
|
||||
'Returns translation according to current locale'
|
||||
);
|
||||
|
||||
i18n::set_locale('de_AT');
|
||||
$this->assertEquals(i18n::_t('i18nTestModule.ENTITY'), 'Entity with "Double Quotes" (de_AT)',
|
||||
'Returns specific regional translation if available'
|
||||
);
|
||||
$this->assertEquals(i18n::_t('i18nTestModule.ADDITION'), 'Addition (de)',
|
||||
'Returns fallback non-regional translation if regional is not available'
|
||||
);
|
||||
|
||||
i18n::set_locale('fr');
|
||||
$this->assertEquals(i18n::_t('i18nTestModule.ENTITY'), '',
|
||||
'Returns empty translation without default string if locale is not found'
|
||||
);
|
||||
$this->assertEquals(i18n::_t('i18nTestModule.ENTITY', 'default'), 'default',
|
||||
'Returns default string if locale is not found'
|
||||
);
|
||||
|
||||
i18n::set_locale($oldLocale);
|
||||
}
|
||||
|
||||
function testIncludeByLocale() {
|
||||
// Looping through modules, so we can test the translation autoloading
|
||||
// Load non-exclusive to retain core class autoloading
|
||||
$classManifest = new SS_ClassManifest($this->alternateBasePath, true, true, false);
|
||||
SS_ClassLoader::instance()->pushManifest($classManifest);
|
||||
|
||||
$adapter = i18n::get_translator('core')->getAdapter();
|
||||
$this->assertTrue($adapter->isAvailable('en'));
|
||||
$this->assertFalse($adapter->isAvailable('de'));
|
||||
$this->assertFalse($adapter->isTranslated('i18nTestModule.ENTITY', 'de'),
|
||||
'Existing unloaded entity not available before call'
|
||||
);
|
||||
$this->assertFalse($adapter->isTranslated('i18nTestModule.ENTITY', 'af'),
|
||||
'Non-existing unloaded entity not available before call'
|
||||
);
|
||||
|
||||
i18n::include_by_locale('de');
|
||||
|
||||
$this->assertTrue($adapter->isAvailable('en'));
|
||||
$this->assertTrue($adapter->isAvailable('de'));
|
||||
$this->assertTrue($adapter->isTranslated('i18nTestModule.ENTITY', null, 'de'), 'Includes module files');
|
||||
$this->assertTrue($adapter->isTranslated('i18nTestTheme1.LAYOUTTEMPLATE', null, 'de'), 'Includes theme files');
|
||||
$this->assertTrue($adapter->isTranslated('i18nTestModule.OTHERENTITY', null, 'de'), 'Includes submodule files');
|
||||
|
||||
SS_ClassLoader::instance()->popManifest();
|
||||
}
|
||||
|
||||
function testRegisterTranslator() {
|
||||
$translator = new Zend_Translate(array(
|
||||
'adapter' => 'i18nTest_CustomTranslatorAdapter',
|
||||
'disableNotices' => true,
|
||||
));
|
||||
|
||||
i18n::register_translator($translator, 'custom', 10);
|
||||
$translators = i18n::get_translators();
|
||||
$this->assertArrayHasKey('custom', $translators[10]);
|
||||
$this->assertType('Zend_Translate', $translators[10]['custom']);
|
||||
$this->assertType('i18nTest_CustomTranslatorAdapter', $translators[10]['custom']->getAdapter());
|
||||
|
||||
i18n::unregister_translator('custom');
|
||||
$translators = i18n::get_translators();
|
||||
$this->assertArrayNotHasKey('custom', $translators[10]);
|
||||
}
|
||||
|
||||
function testMultipleTranslators() {
|
||||
// Looping through modules, so we can test the translation autoloading
|
||||
// Load non-exclusive to retain core class autoloading
|
||||
$classManifest = new SS_ClassManifest($this->alternateBasePath, true, true, false);
|
||||
SS_ClassLoader::instance()->pushManifest($classManifest);
|
||||
|
||||
i18n::set_locale('en_US');
|
||||
|
||||
$this->assertEquals(
|
||||
i18n::_t('i18nTestModule.ENTITY'),
|
||||
'Entity with "Double Quotes"'
|
||||
);
|
||||
$this->assertEquals(
|
||||
i18n::_t('AdapterEntity1', 'AdapterEntity1'),
|
||||
'AdapterEntity1',
|
||||
'Falls back to default string if not found'
|
||||
);
|
||||
|
||||
// Add a new translator
|
||||
$translator = new Zend_Translate(array(
|
||||
'adapter' => 'i18nTest_CustomTranslatorAdapter',
|
||||
'disableNotices' => true,
|
||||
));
|
||||
i18n::register_translator($translator, 'custom', 11);
|
||||
$this->assertEquals(
|
||||
i18n::_t('i18nTestModule.ENTITY'),
|
||||
'i18nTestModule.ENTITY CustomAdapter (en_US)',
|
||||
'Existing entities overruled by adapter with higher priority'
|
||||
);
|
||||
$this->assertEquals(
|
||||
i18n::_t('AdapterEntity1', 'AdapterEntity1'),
|
||||
'AdapterEntity1 CustomAdapter (en_US)',
|
||||
'New entities only defined in new adapter are detected'
|
||||
);
|
||||
|
||||
// Add a second new translator to test priorities
|
||||
$translator = new Zend_Translate(array(
|
||||
'adapter' => 'i18nTest_OtherCustomTranslatorAdapter',
|
||||
'disableNotices' => true,
|
||||
));
|
||||
i18n::register_translator($translator, 'othercustom_lower_prio', 5);
|
||||
$this->assertEquals(
|
||||
i18n::_t('i18nTestModule.ENTITY'),
|
||||
'i18nTestModule.ENTITY CustomAdapter (en_US)',
|
||||
'Adapter with lower priority loses'
|
||||
);
|
||||
|
||||
// Add a third new translator to test priorities
|
||||
$translator = new Zend_Translate(array(
|
||||
'adapter' => 'i18nTest_OtherCustomTranslatorAdapter',
|
||||
'disableNotices' => true,
|
||||
));
|
||||
i18n::register_translator($translator, 'othercustom_higher_prio', 15);
|
||||
$this->assertEquals(
|
||||
i18n::_t('i18nTestModule.ENTITY'),
|
||||
'i18nTestModule.ENTITY OtherCustomAdapter (en_US)',
|
||||
'Adapter with higher priority wins'
|
||||
);
|
||||
|
||||
i18n::unregister_translator('custom');
|
||||
i18n::unregister_translator('othercustom_lower_prio');
|
||||
i18n::unregister_translator('othercustom_higher_prio');
|
||||
|
||||
SS_ClassLoader::instance()->popManifest();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class i18nTest_DataObject extends DataObject implements TestOnly {
|
||||
@ -301,3 +461,39 @@ class i18nTest_Object extends Object implements TestOnly, i18nEntityProvider {
|
||||
}
|
||||
}
|
||||
|
||||
class i18nTest_CustomTranslatorAdapter extends Zend_Translate_Adapter implements TestOnly,i18nTranslateAdapterInterface {
|
||||
protected function _loadTranslationData($filename, $locale, array $options = array()) {
|
||||
return array(
|
||||
$locale => array(
|
||||
'AdapterEntity1' => 'AdapterEntity1 CustomAdapter (' . $locale . ')',
|
||||
'i18nTestModule.ENTITY' => 'i18nTestModule.ENTITY CustomAdapter (' . $locale . ')',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function toString() {
|
||||
return 'i18nTest_CustomTranslatorAdapter';
|
||||
}
|
||||
|
||||
function getFilenameForLocale($locale) {
|
||||
return false; // not file based
|
||||
}
|
||||
}
|
||||
|
||||
class i18nTest_OtherCustomTranslatorAdapter extends Zend_Translate_Adapter implements TestOnly,i18nTranslateAdapterInterface {
|
||||
protected function _loadTranslationData($filename, $locale, array $options = array()) {
|
||||
return array(
|
||||
$locale => array(
|
||||
'i18nTestModule.ENTITY' => 'i18nTestModule.ENTITY OtherCustomAdapter (' . $locale . ')',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function toString() {
|
||||
return 'i18nTest_OtherCustomTranslatorAdapter';
|
||||
}
|
||||
|
||||
function getFilenameForLocale($locale) {
|
||||
return false; // not file based
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ require_once 'Zend/Locale.php';
|
||||
/** Zend_Translate_Adapter */
|
||||
require_once 'Zend/Translate/Adapter.php';
|
||||
|
||||
require_once 'thirdparty/sfYaml/lib/sfYaml.php';
|
||||
// require_once 'thirdparty/sfYaml/lib/sfYaml.php';
|
||||
|
||||
class Translate_Adapter_RailsYaml extends Zend_Translate_Adapter {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user