Browse files

API CHANGE Using Zend_Translate with YAML translation files, replacin…

…g the $lang global and PHP files in the /lang folders.
  • Loading branch information...
1 parent 0a0be63 commit bd23a07bbadd1b6d2a11b5fe7a4494d3a68c901f @chillu chillu committed Nov 26, 2011
Showing with 774 additions and 255 deletions.
  1. +1 −1 core/manifest/ConfigManifest.php
  2. +183 −188 i18n/i18n.php
  3. +19 −0 i18n/i18nRailsYamlAdapter.php
  4. +87 −0 i18n/i18nSSLegacyAdapter.php
  5. +23 −0 i18n/i18nTranslateAdapterInterface.php
  6. +2 −7 lang/he-IL.yml
  7. +4 −0 tests/i18n/_fakewebroot/i18nothermodule/lang/de.yml
  8. +13 −0 tests/i18n/_fakewebroot/i18nothermodule/lang/de_DE.php
  9. +4 −0 tests/i18n/_fakewebroot/i18nothermodule/lang/en.yml
  10. +5 −0 tests/i18n/_fakewebroot/i18nothermodule/lang/en_US.php
  11. 0 tests/i18n/_fakewebroot/i18ntestmodule/code/subfolder/_config.php
  12. +3 −0 tests/i18n/_fakewebroot/i18ntestmodule/code/subfolder/lang/de.yml
  13. +3 −0 tests/i18n/_fakewebroot/i18ntestmodule/code/subfolder/lang/en.yml
  14. +15 −0 tests/i18n/_fakewebroot/i18ntestmodule/lang/de.yml
  15. +3 −0 tests/i18n/_fakewebroot/i18ntestmodule/lang/de_AT.yml
  16. +15 −0 tests/i18n/_fakewebroot/i18ntestmodule/lang/en.yml
  17. +3 −0 tests/i18n/_fakewebroot/i18ntestmodule/lang/fr.yml
  18. +11 −0 tests/i18n/_fakewebroot/themes/testtheme1/lang/de.yml
  19. +13 −0 tests/i18n/_fakewebroot/themes/testtheme1/lang/de_DE.php
  20. +11 −0 tests/i18n/_fakewebroot/themes/testtheme1/lang/en.yml
  21. +5 −0 tests/i18n/_fakewebroot/themes/testtheme1/lang/en_US.php
  22. +3 −0 tests/i18n/_fakewebroot/themes/testtheme2/lang/de.yml
  23. +3 −0 tests/i18n/_fakewebroot/themes/testtheme2/lang/en.yml
  24. +90 −0 tests/i18n/i18nSSLegacyAdapterTest.php
  25. +254 −58 tests/i18n/i18nTest.php
  26. +1 −1 thirdparty/zend_translate_railsyaml/library/Translate/Adapter/RailsYAML.php
View
2 core/manifest/ConfigManifest.php
@@ -178,7 +178,7 @@ public function addYAMLConfigFile($basename, $pathname, $depth) {
// 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
View
371 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.
*
@@ -85,6 +88,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
* (framework/javascript/i18n.js) and all necessary lang files (e.g. framework/lang/de_DE.js)
@@ -1452,24 +1460,98 @@ public static function get_time_format() {
* @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();
+ $adapter->setLocale($locale);
+
+ // if language table isn't loaded for this locale, get it for each of the modules
+ if(!$adapter->isAvailable($locale)) i18n::include_by_locale($locale);
+
+ $translation = $adapter->translate($entity, $locale);
+
+ // Return translation only if we found a match thats not the entity itself (Zend fallback)
+ if($translation && $translation != $entity) return $translation;
+ }
+ }
- // get current locale (either default or user preference)
- $locale = i18n::get_locale();
+ // Fall back to default string argument
+ return $string;
+ }
- // parse $entity into its parts
- $entityParts = explode('.',$entity);
- $realEntity = array_pop($entityParts);
- $class = implode('.',$entityParts);
+ /**
+ * @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');
+ }
- // 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);
+ 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");
- // fallback to the passed $string if no translation is present
- $transEntity = isset($lang[$locale][$class][$realEntity]) ? $lang[$locale][$class][$realEntity] : $string;
+ // Ensure it's not there. If it is, we're replacing it. It may exist in a different priority.
+ self::unregister_translator($name);
- // entities can be stored in both array and literal values in the language tables
- return (is_array($transEntity) ? $transEntity[0] : $transEntity);
+ // 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 @@ static function get_locale_list() {
* @return array
*/
static function get_existing_translations() {
- $locales = array();
+ $localeWithTitles = array();
- $baseDir = Director::baseFolder();
- $modules = scandir($baseDir);
- foreach($modules as $module) {
- if($module[0] == '.') continue;
-
- $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]];
- }
- }
- }
+ 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(!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);
+
+ $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 @@ static function validate_locale($locale) {
* @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 @@ static function default_locale() {
* @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;
+ // 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)
+ );
+ }
+ }
- 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 themes
- $themesBase = $base . '/themes';
-
- if(is_dir($themesBase) && SSViewer::current_theme()) {
- foreach(scandir($themesBase) as $theme) {
- if(
- strpos($theme, SSViewer::current_theme()) === 0
- && file_exists($file = "$themesBase/$theme/lang/$locale.php")
- ) {
- if ($force_load) include($file);
- else include_once($file);
+ // Load translations from themes
+ // 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("{$themesBase}/{$theme}/lang/")
+ ) {
+ 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();
}
/**
@@ -1849,113 +1940,18 @@ static function include_by_locale($locale, $load_plugins = true, $force_load = f
*/
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 @@ public static function get_template_global_variables() {
'get_locale',
);
}
+
}
-
-
View
19 i18n/i18nRailsYamlAdapter.php
@@ -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";
+ }
+}
View
87 i18n/i18nSSLegacyAdapter.php
@@ -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()));
+ }
+}
View
23 i18n/i18nTranslateAdapterInterface.php
@@ -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);
+}
View
9 lang/he-IL.yml
@@ -131,7 +131,7 @@ he-IL:
GREETING: "ברוך הבא"
INTERFACELANG: "שפת ממשק"
LOGGEDINAS: |
- אתה מחובר כ %s.
+ אתה מחובר כ %s.
MOBILE: "נייד"
NAME: "שם"
NEWPASSWORD: "סיסמא חדשה"
@@ -145,7 +145,7 @@ he-IL:
SURNAME: "שם משפחה"
VALIDATIONMEMBEREXISTS: "קיים כבר משתמש עם כתובת דואר זו."
WELCOMEBACK: |
- ברוך הבא, %s
+ ברוך הבא, %s
YOUROLDPASSWORD: "הסיסמא הישנה שלך"
belongs_many_many_Groups: "קבוצות"
db_LockedOutUntil: "נעול עד "
@@ -205,11 +205,6 @@ he-IL:
LOSTPASSWORDHEADER: "איבדת סיסמא"
NOTEPAGESECURED: "עמוד זה אינו מאובטח. הכנס את הפרטים שלך להלן ונשלח אליך מייד."
NOTERESETPASSWORD: "הכנס את כתובת הדואר האלקטרוני שלך ונשלח אליך קישור שבעזרתו תוכל לאפס את הסיסמא שלך"
- PASSWORDSENTHEADER: |
- קישור לאיפוס סיסמא נשלח ל '%s'
- PASSWORDSENTTEXT: |
- תודה רבה! קישור לאיפוס הסיסמא נשלח ל '%s'.
- SecurityAdmin:
ADDMEMBER: "הוסף חבר"
EDITPERMISSIONS: "ערוך הרשאות וכתובות IP לכל קבוצה"
MENUTITLE: "אבטחה"
View
4 tests/i18n/_fakewebroot/i18nothermodule/lang/de.yml
@@ -0,0 +1,4 @@
+de:
+ i18nOtherModule:
+ ENTITY: Other Module Entity (de)
+ MAINTEMPLATE: Main Template Other Module (de)
View
13 tests/i18n/_fakewebroot/i18nothermodule/lang/de_DE.php
@@ -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)';
View
4 tests/i18n/_fakewebroot/i18nothermodule/lang/en.yml
@@ -0,0 +1,4 @@
+en:
+ i18nOtherModule:
+ ENTITY: Other Module Entity
+ MAINTEMPLATE: Main Template Other Module
View
5 tests/i18n/_fakewebroot/i18nothermodule/lang/en_US.php
@@ -0,0 +1,5 @@
+<?php
+
+global $lang;
+
+$lang['en_US']['i18nOtherModule']['LEGACY'] = 'Legacy translation';
View
0 tests/i18n/_fakewebroot/i18ntestmodule/code/subfolder/_config.php
No changes.
View
3 tests/i18n/_fakewebroot/i18ntestmodule/code/subfolder/lang/de.yml
@@ -0,0 +1,3 @@
+de:
+ i18nTestModule:
+ OTHERENTITY: Other Entity (de)
View
3 tests/i18n/_fakewebroot/i18ntestmodule/code/subfolder/lang/en.yml
@@ -0,0 +1,3 @@
+en:
+ i18nTestModule:
+ OTHERENTITY: Other Entity
View
15 tests/i18n/_fakewebroot/i18ntestmodule/lang/de.yml
@@ -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)
View
3 tests/i18n/_fakewebroot/i18ntestmodule/lang/de_AT.yml
@@ -0,0 +1,3 @@
+de_AT:
+ i18nTestModule:
+ ENTITY: Entity with "Double Quotes" (de_AT)
View
15 tests/i18n/_fakewebroot/i18ntestmodule/lang/en.yml
@@ -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
View
3 tests/i18n/_fakewebroot/i18ntestmodule/lang/fr.yml
@@ -0,0 +1,3 @@
+fr:
+ i18nTestModule:
+ ENTITY: Entity with "Double Quotes" (fr)
View
11 tests/i18n/_fakewebroot/themes/testtheme1/lang/de.yml
@@ -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)
View
13 tests/i18n/_fakewebroot/themes/testtheme1/lang/de_DE.php
@@ -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)';
View
11 tests/i18n/_fakewebroot/themes/testtheme1/lang/en.yml
@@ -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
View
5 tests/i18n/_fakewebroot/themes/testtheme1/lang/en_US.php
@@ -0,0 +1,5 @@
+<?php
+
+global $lang;
+
+$lang['en_US']['i18nOtherModule']['LEGACYTHEME'] = 'Legacy translation';
View
3 tests/i18n/_fakewebroot/themes/testtheme2/lang/de.yml
@@ -0,0 +1,3 @@
+de:
+ i18nTestTheme2:
+ MAINTEMPLATE: Theme2 Main Template (de)
View
3 tests/i18n/_fakewebroot/themes/testtheme2/lang/en.yml
@@ -0,0 +1,3 @@
+en:
+ i18nTestTheme2:
+ MAINTEMPLATE: Theme2 Main Template
View
90 tests/i18n/i18nSSLegacyAdapterTest.php
@@ -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'
+ // );
+ }
+
+}
View
312 tests/i18n/i18nTest.php
@@ -1,4 +1,6 @@
<?php
+require_once 'Zend/Translate.php';
+
/**
* @package framework
* @subpackage tests
@@ -29,18 +31,36 @@ function 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.
- $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 @@ function testTimeFormatCustom() {
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 @@ function testDataObjectFieldLabels() {
}
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 @@ function testProvideI18nEntities() {
}
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 @@ function testTemplateTranslation() {
);
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(
@@ -213,29 +248,6 @@ function testGetLocaleFromLang() {
$this->assertEquals('de_DE', i18n::get_locale_from_lang('de_DE'));
$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');
@@ -244,12 +256,160 @@ function testValidateLocale() {
$this->assertFalse(i18n::validate_locale('xx_XX'), 'Unknown locale in correct format is not valid');
$this->assertFalse(i18n::validate_locale(''), 'Empty string is not valid');
}
+
+ 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'
+ );
- static function translationTestPlugin($locale) {
- $result = array();
- $result["en_US"]["i18nTestProvider"]["foo"] = "baz_en";
- return $result;
+ 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 @@ function provideI18nEntities() {
}
}
+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
+ }
+}
View
2 thirdparty/zend_translate_railsyaml/library/Translate/Adapter/RailsYAML.php
@@ -6,7 +6,7 @@
/** 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 {

0 comments on commit bd23a07

Please sign in to comment.