diff --git a/.upgrade.yml b/.upgrade.yml index f2452214a83..d9d6a5a6316 100644 --- a/.upgrade.yml +++ b/.upgrade.yml @@ -454,11 +454,10 @@ mappings: i18nRailsYamlAdapter: SilverStripe\i18n\i18nRailsYamlAdapter i18nSSLegacyAdapter: SilverStripe\i18n\i18nSSLegacyAdapter i18nSSLegacyAdapter_Iterator: SilverStripe\i18n\i18nSSLegacyAdapter_Iterator - i18nTextCollector: SilverStripe\i18n\i18nTextCollector - i18nTextCollector_Writer: SilverStripe\i18n\i18nTextCollector_Writer - i18nTextCollector_Writer_Php: SilverStripe\i18n\i18nTextCollector_Writer_Php - i18nTextCollector_Writer_RailsYaml: SilverStripe\i18n\i18nTextCollector_Writer_RailsYaml - i18nTextCollector_Parser: SilverStripe\i18n\i18nTextCollector_Parser + i18nTextCollector: SilverStripe\i18n\TextCollection\i18nTextCollector + i18nTextCollector_Writer: SilverStripe\i18n\Messages\Writer + i18nTextCollector_Writer_RailsYaml: SilverStripe\i18n\Messages\YamlWriter + i18nTextCollector_Parser: SilverStripe\i18n\TextCollection\Parser i18nTranslateAdapterInterface: SilverStripe\i18n\i18nTranslateAdapterInterface SilverStripe\Framework\Logging\DebugViewFriendlyErrorFormatter: SilverStripe\Logging\DebugViewFriendlyErrorFormatter SilverStripe\Framework\Logging\DetailedErrorFormatter: SilverStripe\Logging\DetailedErrorFormatter diff --git a/_config/i18n.yml b/_config/i18n.yml index 5af9bd4f773..a5a0bfe08c9 100644 --- a/_config/i18n.yml +++ b/_config/i18n.yml @@ -14,7 +14,47 @@ SilverStripe\i18n\i18n: module_priority: - other_modules --- +name: i18nMessages +--- +SilverStripe\Core\Injector\Injector: + # Custom yml loader for localisation messages + SilverStripe\i18n\Messages\Reader: + class: SilverStripe\i18n\Messages\YamlReader + SilverStripe\i18n\Messages\Writer: + class: SilverStripe\i18n\Messages\YamlWriter + Symfony\Component\Translation\Loader\LoaderInterface: + class: SilverStripe\i18n\Messages\Symfony\ModuleYamlLoader + properties: + Reader: %$SilverStripe\i18n\Messages\Reader + # Ensure our cache respects ModuleYamlLoader's self-invalidation + # @see DirectoryListResource::isFresh() + # Note: This could be replaced with a more aggressive cache if necessary on a live environment + Symfony\Component\Config\ConfigCacheFactoryInterface: + class: Symfony\Component\Config\ResourceCheckerConfigCacheFactory + constructor: + 0: [ %$Symfony\Component\Config\Resource\SelfCheckingResourceChecker ] + # Create default translator with standard cache path and our custom loader + Symfony\Component\Translation\TranslatorInterface: + class: Symfony\Component\Translation\Translator + constructor: + 0: 'en' + 1: null + 2: `TEMP_FOLDER` + properties: + ConfigCacheFactory: %$Symfony\Component\Config\ConfigCacheFactoryInterface + calls: + FallbackLocales: [ setFallbackLocales, [['en']]] + Loader: [ addLoader, ['ss', %$Symfony\Component\Translation\Loader\LoaderInterface ]] + # Set this translator as our message provider for silverstripe's i18n + SilverStripe\i18n\Messages\MessageProvider: + class: SilverStripe\i18n\Messages\Symfony\SymfonyMessageProvider + properties: + Translator: %$Symfony\Component\Translation\TranslatorInterface +--- Name: textcollector --- SilverStripe\Core\Injector\Injector: - SilverStripe\i18n\i18nTextCollector_Writer: SilverStripe\i18n\i18nTextCollector_Writer_RailsYaml + SilverStripe\i18n\TextCollection\i18nTextCollector: + properties: + Reader: %$SilverStripe\i18n\Messages\Reader + Writer: %$SilverStripe\i18n\Messages\Writer diff --git a/admin/code/CMSMenu.php b/admin/code/CMSMenu.php index e237d561e13..94e783f3b6c 100644 --- a/admin/code/CMSMenu.php +++ b/admin/code/CMSMenu.php @@ -428,7 +428,10 @@ public function provideI18nEntities() foreach ($cmsClasses as $cmsClass) { $defaultTitle = LeftAndMain::menu_title($cmsClass, false); $ownerModule = i18n::get_owner_module($cmsClass); - $entities["{$cmsClass}.MENUTITLE"] = array($defaultTitle, 'Menu title', $ownerModule); + $entities["{$cmsClass}.MENUTITLE"] = [ + 'default' => $defaultTitle, + 'module' => $ownerModule + ]; } return $entities; } diff --git a/admin/code/ModelAdmin.php b/admin/code/ModelAdmin.php index 75b5dbdaa33..6809656cc0f 100644 --- a/admin/code/ModelAdmin.php +++ b/admin/code/ModelAdmin.php @@ -464,7 +464,7 @@ public function ImportForm() } $fields = new FieldList( - new HiddenField('ClassName', _t('ModelAdmin.CLASSTYPE'), $this->modelClass), + new HiddenField('ClassName', false, $this->modelClass), new FileField('_CsvFile', false) ); diff --git a/composer.json b/composer.json index a68f48448d4..185e092cd09 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,9 @@ "league/flysystem": "~1.0.12", "symfony/yaml": "~2.7", "embed/embed": "^2.6", - "swiftmailer/swiftmailer": "~5.4" + "swiftmailer/swiftmailer": "~5.4", + "symfony/config": "^2.8|^3", + "symfony/translation": "^2.8|^3" }, "require-dev": { "phpunit/PHPUnit": "~4.8", diff --git a/docs/en/02_Developer_Guides/05_Extending/05_Injector.md b/docs/en/02_Developer_Guides/05_Extending/05_Injector.md index b3a414ed840..aa3785fbff8 100644 --- a/docs/en/02_Developer_Guides/05_Extending/05_Injector.md +++ b/docs/en/02_Developer_Guides/05_Extending/05_Injector.md @@ -121,6 +121,21 @@ As well as properties, method calls can also be specified: - [ pushHandler, [ %$DefaultHandler ] ] +## Using constants as variables + +Any of the core constants can be used as a service argument by quoting with back ticks "`". + + + :::yaml + CachingService: + class: SilverStripe\Cache\CacheProvider + properties: + CacheDir: `TEMP_DIR` + + +Note: undefined variables will be replaced with null + + ## Factories Some services require non-trivial construction which means they must be created by a factory class. To do this, create diff --git a/docs/en/02_Developer_Guides/13_i18n/index.md b/docs/en/02_Developer_Guides/13_i18n/index.md index 81cc1fd850a..66e8c934c79 100644 --- a/docs/en/02_Developer_Guides/13_i18n/index.md +++ b/docs/en/02_Developer_Guides/13_i18n/index.md @@ -166,12 +166,68 @@ All strings passed through the `_t()` function will be collected in a separate l The `_t()` function is the main gateway to localized text, and takes four parameters, all but the first being optional. It can be used to translate strings in both PHP files and template files. The usage for each case is described below. - * **$entity:** Unique identifier, composed by a namespace and an entity name, with a dot separating them. Both are arbitrary names, although by convention we use the name of the containing class or template. Use this identifier to reference the same translation elsewhere in your code. - * **$string:** (optional) The original language string to be translated. Only needs to be declared once, and gets picked up the [text collector](#collecting-text). - * **$string:** (optional) Natural language comment (particularly short phrases and individual words) -are very context dependent. This parameter allows the developer to convey this information -to the translator. - * **$array::** (optional) An array of injecting variables into the second parameter +* **$entity:** Unique identifier, composed by a namespace and an entity name, with a dot + separating them. Both are arbitrary names, although by convention we use the name of + the containing class or template. Use this identifier to reference the same translation + elsewhere in your code. +* **$default:** The original language string to be translated. This should be declared + whenever used, and will get picked up the [text collector](#collecting-text). +* **$string:** (optional) Natural language comment (particularly short phrases and individual words) + are very context dependent. This parameter allows the developer to convey this information + to the translator. +* **$injection::** (optional) An array of injecting variables into the second parameter + + +## Pluralisation + +i18n also supports locale-respective pluralisation rules. Many languages have more than two plural forms, +unlike English which has two only; One for the singular, and another for any other number. + +More information on what forms these plurals can take for various locales can be found on the +[CLDR documentation](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html) + +The ability to pluralise strings is provided through the `i18n::_t` method when supplied with a +`{count}` argument and `|` pipe-delimiter provided with the default string. + +Plural forms can also be explicitly declared via the i18nEntityProvider interface in array-format +with both a 'one' and 'other' key (as per the CLDR for the default `en` language). + +For instance, this is an example of how to correctly declare pluralisations for an object + + + :::php + class MyObject extends DataObject, implements i18nEntityProvider + { + public function provideI18nEntities() + { + return [ + 'MyObject.SINGULAR_NAME' => 'object', + 'MyObject.PLURAL_NAME' => 'objects', + 'MyObject.PLURALS' => [ + 'one' => 'An object', + 'other' => '{count} objects', + ], + ]; + } + } + + +In YML format this will be expressed as the below. This follows the +[ruby i18n convention](guides.rubyonrails.org/i18n.html#pluralization) for plural forms. + + + :::yaml + en: + MyObject: + SINGULAR_NAME: 'object' + PLURAL_NAME: 'objects' + PLURALS: + one: 'An object', + other: '{count} objects' + + +Note: i18nTextCollector support for pluralisation is not yet available. +Please ensure that any required plurals are exposed via provideI18nEntities. #### Usage in PHP Files @@ -180,15 +236,15 @@ to the translator. // Simple string translation _t('LeftAndMain.FILESIMAGES','Files & Images'); - // Using the natural languate comment parameter to supply additional context information to translators - _t('LeftAndMain.HELLO','Site content','Menu title'); - // Using injection to add variables into the translated strings. _t('CMSMain.RESTORED', "Restored {value} successfully", - 'This is a message when restoring a broken part of the CMS', array('value' => $itemRestored) ); + + // Plurals are invoked via a `|` pipe-delimeter with a {count} argument + _t('MyObject.PLURALS', 'An object|{count} objects', [ 'count' => '$count ]); + #### Usage in Template Files @@ -207,11 +263,12 @@ the PHP version of the function. // Simple string translation <%t Namespace.Entity "String to translate" %> - // Using the natural languate comment parameter to supply additional context information to translators - <%t SearchResults.NoResult "There are no results matching your query." is "A message displayed to users when the search produces no results." %> - // Using injection to add variables into the translated strings (note that $Name and $Greeting must be available in the current template scope). <%t Header.Greeting "Hello {name} {greeting}" name=$Name greeting=$Greeting %> + + // Plurals follow the same convention, required a `|` and `{count}` in the default string + <%t MyObject.PLURALS 'An item|{count} items' count=$Count %> + #### Caching in Template Files with locale switching @@ -279,13 +336,14 @@ Each module can have one language table per locale, stored by convention in the The translation is powered by [Zend_Translate](http://framework.zend.com/manual/current/en/modules/zend.i18n.translating.html), which supports different translation adapters, dealing with different storage formats. -By default, SilverStripe 3.x uses a YAML format (through the [Zend_Translate_RailsYAML adapter](https://github.com/chillu/zend_translate_railsyaml)). +By default, SilverStripe uses a YAML format which is loaded via the +[symfony/translate](http://symfony.com/doc/current/translation.html) library. Example: framework/lang/en.yml (extract) en: ImageUploader: - Attach: 'Attach %s' + Attach: 'Attach {title}' UploadField: NOTEADDFILES: 'You can add files once you have saved for the first time.' @@ -293,7 +351,7 @@ Translation table: framework/lang/de.yml (extract) de: ImageUploader: - ATTACH: '%s anhängen' + ATTACH: '{title} anhängen' UploadField: NOTEADDFILES: 'Sie können Dateien hinzufügen sobald Sie das erste mal gespeichert haben' @@ -301,24 +359,6 @@ Note that translations are cached across requests. The cache can be cleared through the `?flush=1` query parameter, or explicitly through `Zend_Translate::getCache()->clean(Zend_Cache::CLEANING_MODE_ALL)`. -
- * class MyTestClass implements i18nEntityProvider {
- * function provideI18nEntities() {
- * $entities = array();
- * foreach($this->stat('my_static_array) as $key => $value) {
- * $entities["MyTestClass.my_static_array_{$key}"] = array(
- * $value,
+ * Returns the list of provided translations for this object.
*
- * 'My context description'
- * );
- * }
- * return $entities;
- * }
+ * Note: Pluralised forms are always returned in array format.
*
- * public static function my_static_array() {
- * $t_my_static_array = array();
- * foreach(self::$my_static_array as $k => $v) {
- * $t_my_static_array[$k] = _t("MyTestClass.my_static_array_{$key}", $v);
- * }
- * return $t_my_static_array;
- * }
+ * Example usage:
+ *
+ * class MyTestClass implements i18nEntityProvider
+ * {
+ * public function provideI18nEntities()
+ * {
+ * $entities = [];
+ * foreach($this->stat('my_static_array) as $key => $value) {
+ * $entities["MyTestClass.my_static_array_{$key}"] = $value;
+ * }
+ * $entities["MyTestClass.PLURALS"] = [
+ * 'one' => 'A test class',
+ * 'other' => '{count} test classes',
+ * ]
+ * return $entities;
+ * }
* }
*
*
* Example usage in {@link DataObject->provideI18nEntities()}.
*
- * You can ask textcollector to add the provided entity to a different module
- * than the class is contained in by adding a 4th argument to the array:
- *
- * class MyTestClass implements i18nEntityProvider {
- * function provideI18nEntities() {
- * $entities = array();
- * $entities["MyOtherModuleClass.MYENTITY"] = array(
- * $value,
+ * You can ask textcollector to add the provided entity to a different module.
+ * Simply wrap the returned value for any item in an array with the format:
+ * [ 'default' => $defaultValue, 'module' => $module ]
*
- * 'My context description',
- * 'myothermodule'
- * );
- * }
- * return $entities;
+ *
+ * class MyTestClass implements i18nEntityProvider
+ * {
+ * public function provideI18nEntities()
+ * {
+ * $entities = [
+ * 'MyOtherModuleClass.MYENTITY' => [
+ * 'default' => $value,
+ * 'module' => 'myothermodule',
+ * ]
+ * ];
+ * }
+ * return $entities;
* }
*
*
- * @return array All entites in an associative array, with
- * entity name as the key, and a numerical array of pseudo-arguments
- * for _t() as a value.
+ * @return array Map of keys to default values, which are strings in the default case,
+ * and array-form for pluralisations.
*/
public function provideI18nEntities();
}
diff --git a/src/i18n/i18nRailsYamlAdapter.php b/src/i18n/i18nRailsYamlAdapter.php
deleted file mode 100644
index 9bc96a7beda..00000000000
--- a/src/i18n/i18nRailsYamlAdapter.php
+++ /dev/null
@@ -1,21 +0,0 @@
-string = $string;
- $this->pos = 0;
- $this->depth = 0;
- $this->regexps = array();
- }
-
- public function Translate__construct(&$res)
- {
- self::$currentEntity = array(null, null, null); //start with empty array
- }
-
- public function Translate_Entity(&$res, $sub)
- {
- self::$currentEntity[0] = $sub['text']; //entity
- }
-
- public function Translate_Default(&$res, $sub)
- {
- self::$currentEntity[1] = $sub['String']['text']; //value
- }
-
- public function Translate_Context(&$res, $sub)
- {
- self::$currentEntity[2] = $sub['String']['text']; //comment
- }
-
- public function Translate__finalise(&$res)
- {
- // set the entity name and the value (default), as well as the context (comment)
- // priority is no longer used, so that is blank
- self::$entities[self::$currentEntity[0]] = array(self::$currentEntity[1], null, self::$currentEntity[2]);
- }
-
- /**
- * Parses a template and returns any translatable entities
- */
- public static function GetTranslatables($template)
- {
- self::$entities = array();
-
- // Run the parser and throw away the result
- $parser = new i18nTextCollector_Parser($template);
- if (substr($template, 0, 3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
- $parser->pos = 3;
- }
- $parser->match_TopTemplate();
-
- return self::$entities;
- }
-}
diff --git a/src/i18n/i18nTextCollector_Writer_RailsYaml.php b/src/i18n/i18nTextCollector_Writer_RailsYaml.php
deleted file mode 100644
index 43b2a57a29c..00000000000
--- a/src/i18n/i18nTextCollector_Writer_RailsYaml.php
+++ /dev/null
@@ -1,62 +0,0 @@
-getYaml($entities, $locale));
- fclose($fh);
- } else {
- throw new LogicException("Cannot write language file! Please check permissions of $langFile");
- }
-
- return true;
- }
-
- public function getYaml($entities, $locale)
- {
- // Unflatten array
- $entitiesNested = array();
- foreach ($entities as $entity => $spec) {
- // Legacy support: Don't count *.ss as namespace
- $entity = preg_replace('/\.ss\./', '___ss.', $entity);
- $parts = explode('.', $entity);
- $currLevel = &$entitiesNested;
- while ($part = array_shift($parts)) {
- $part = str_replace('___ss', '.ss', $part);
- if (!isset($currLevel[$part])) {
- $currLevel[$part] = array();
- }
- $currLevel = &$currLevel[$part];
- }
- $currLevel = $spec[0];
- }
-
- // Write YAML
- $dumper = new Dumper();
- $dumper->setIndentation(2);
- // TODO Dumper can't handle YAML comments, so the context information is currently discarded
- $result = $dumper->dump(array($locale => $entitiesNested), 99);
- return $result;
- }
-}
diff --git a/src/i18n/i18nTranslateAdapterInterface.php b/src/i18n/i18nTranslateAdapterInterface.php
deleted file mode 100644
index 5068fac8575..00000000000
--- a/src/i18n/i18nTranslateAdapterInterface.php
+++ /dev/null
@@ -1,22 +0,0 @@
-assertEquals('Three', $another->filters[2]);
}
+ public function testConstantUsage()
+ {
+ $injector = new Injector();
+ $services = array(
+ AnotherService::class => array(
+ 'properties' => array(
+ 'filters' => array(
+ '`BASE_PATH`',
+ '`TEMP_FOLDER`',
+ '`NOT_DEFINED`',
+ 'THIRDPARTY_DIR' // Not back-tick escaped
+ )
+ ),
+ )
+ );
+
+ $injector->load($services);
+ $another = $injector->get(AnotherService::class);
+ $this->assertEquals(
+ [
+ BASE_PATH,
+ TEMP_FOLDER,
+ null,
+ 'THIRDPARTY_DIR',
+ ],
+ $another->filters
+ );
+ }
+
public function testAutoSetInjector()
{
$injector = new Injector();
diff --git a/tests/php/Security/MemberAuthenticatorTest.php b/tests/php/Security/MemberAuthenticatorTest.php
index 50d19dbf8b7..893a3f670b9 100644
--- a/tests/php/Security/MemberAuthenticatorTest.php
+++ b/tests/php/Security/MemberAuthenticatorTest.php
@@ -172,7 +172,10 @@ public function testAuthenticateByTempID()
);
$form->restoreFormState();
$this->assertEmpty($result);
- $this->assertEquals(_t('Member.ERRORWRONGCRED'), $form->getMessage());
+ $this->assertEquals(
+ _t('Member.ERRORWRONGCRED', 'The provided details don\'t seem to be correct. Please try again.'),
+ $form->getMessage()
+ );
$this->assertEquals(ValidationResult::TYPE_ERROR, $form->getMessageType());
$this->assertEquals(ValidationResult::CAST_TEXT, $form->getMessageCast());
}
diff --git a/tests/php/i18n/YamlReaderTest.php b/tests/php/i18n/YamlReaderTest.php
new file mode 100644
index 00000000000..2f285723ac2
--- /dev/null
+++ b/tests/php/i18n/YamlReaderTest.php
@@ -0,0 +1,41 @@
+read('en', $path);
+ $expected = [
+ 'NONAMESPACE' => 'Include Entity without Namespace',
+ 'SPRINTFNONAMESPACE' => 'My replacement no namespace: %s',
+ 'SPRINTFINCLUDENONAMESPACE' => 'My include replacement no namespace: %s',
+ 'LAYOUTTEMPLATENONAMESPACE' => 'Layout Template no namespace',
+ 'i18nTestModule.ENTITY' => 'Entity with "Double Quotes"',
+ 'i18nTestModule.ADDITION' => 'Addition',
+ 'i18nTestModule.MAINTEMPLATE' => 'Main Template',
+ 'i18nTestModule.WITHNAMESPACE' => 'Include Entity with Namespace',
+ 'i18nTestModule.LAYOUTTEMPLATE' => 'Layout Template',
+ 'i18nTestModule.SPRINTFNAMESPACE' => 'My replacement: %s',
+ 'i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE' => 'My include replacement: %s',
+ 'i18nTestModule.PLURALS' => [
+ 'one' => 'A test',
+ 'other' => '{count} tests',
+ ],
+ 'Month.PLURALS' => [
+ 'one' => 'A month',
+ 'other' => '{count} months',
+ ],
+ ];
+ $this->assertEquals($expected, $output);
+ }
+}
diff --git a/tests/php/i18n/YamlWriterTest.php b/tests/php/i18n/YamlWriterTest.php
new file mode 100644
index 00000000000..c8927336fbd
--- /dev/null
+++ b/tests/php/i18n/YamlWriterTest.php
@@ -0,0 +1,64 @@
+ 'Text',
+ 'Level1.OtherEntityName' => 'Other Text',
+ 'Level1.Plurals' => [
+ 'context' => 'Some ignored context',
+ 'one' => 'An item',
+ 'other' => '{count} items',
+ ],
+ 'Level1.PluralString1' => 'An item|{count} items',
+ 'Level1.PluralString2' => [
+ 'context' => 'Another ignored context',
+ 'default' => 'An item|{count} items',
+ ],
+ // Some near-false-positives for plurals
+ 'Level1.NotPlural1' => 'Not a plural|string', // no count
+ 'Level1.NotPlural2' => 'Not|a|plural|string{count}', // unexpected number
+ 'Level1.NotPlural3' => 'Not a plural string {count}', // no pipe
+ 'Level1.BoolTest' => 'True',
+ 'Level1.FlagTest' => 'No',
+ 'Level1.TextTest' => 'Maybe',
+ 'Template.ss.Key' => 'Template var',
+ 'TopLevel' => 'The Top',
+ ];
+ $yaml = <<assertEquals($yaml, Convert::nl2os($writer->getYaml($entities, 'de')));
+ }
+}
diff --git a/tests/php/i18n/i18nTest.php b/tests/php/i18n/i18nTest.php
index cbd160520e7..8cbbb4892c1 100644
--- a/tests/php/i18n/i18nTest.php
+++ b/tests/php/i18n/i18nTest.php
@@ -2,136 +2,30 @@
namespace SilverStripe\i18n\Tests;
-use SilverStripe\Assets\Filesystem;
+use InvalidArgumentException;
use SilverStripe\Control\Director;
use SilverStripe\Core\Convert;
-use SilverStripe\Core\Manifest\ClassManifest;
-use SilverStripe\Core\Manifest\ClassLoader;
+use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\i18n\i18n;
-use SilverStripe\i18n\i18nRailsYamlAdapter;
-use SilverStripe\i18n\Tests\i18nTest\CustomTranslatorAdapter;
-use SilverStripe\i18n\Tests\i18nTest\MyObject;
-use SilverStripe\i18n\Tests\i18nTest\MySubObject;
-use SilverStripe\i18n\Tests\i18nTest\OtherCustomTranslatorAdapter;
-use SilverStripe\i18n\Tests\i18nTest\TestDataObject;
-use SilverStripe\i18n\Tests\i18nTest\TestObject;
+use SilverStripe\i18n\Messages\MessageProvider;
+use SilverStripe\i18n\Messages\Symfony\SymfonyMessageProvider;
use SilverStripe\View\ArrayData;
use SilverStripe\View\SSViewer;
-use SilverStripe\View\ThemeResourceLoader;
-use SilverStripe\View\ThemeManifest;
-use Zend_Translate;
-
-require_once 'Zend/Translate.php';
class i18nTest extends SapphireTest
{
-
- /**
- * @var string $tmpBasePath Used to write language files.
- * We don't want to store them inside framework (or in any web-accessible place)
- * in case something goes wrong with the file parsing.
- */
- protected $alternateBaseSavePath;
-
- /**
- * @var string $alternateBasePath Fake webroot with a single module
- * /i18ntestmodule which contains some files with _t() calls.
- */
- protected $alternateBasePath;
-
- protected $extraDataObjects = [
- TestDataObject::class
- ];
-
- protected $preloadClasses = [
- OtherCustomTranslatorAdapter::class,
- CustomTranslatorAdapter::class,
- TestObject::class,
- MySubObject::class,
- MyObject::class
- ];
-
+ use i18nTestManifest;
public function setUp()
{
parent::setUp();
-
- // Force loading of classes before manifests potentially break autoloading
- foreach ($this->preloadClasses as $class) {
- if (!class_exists($class)) {
- throw new \LogicException("Could not load class $class");
- }
- }
-
- $s = DIRECTORY_SEPARATOR;
- $this->alternateBasePath = __DIR__ . $s . 'i18nTest' . $s . "_fakewebroot";
- $this->alternateBaseSavePath = TEMP_FOLDER . $s . 'i18nTextCollectorTest_webroot';
- Filesystem::makeFolder($this->alternateBaseSavePath);
- Director::config()->update('alternate_base_folder', $this->alternateBasePath);
-
- // Replace old template loader with new one with alternate base path
- $this->_oldLoader = ThemeResourceLoader::instance();
- ThemeResourceLoader::set_instance($loader = new ThemeResourceLoader($this->alternateBasePath));
- $loader->addSet(
- '$default',
- new ThemeManifest(
- $this->alternateBasePath,
- project(),
- false,
- true
- )
- );
-
- SSViewer::config()->update('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::class,
- 'locale' => i18n::config()->get('default_locale'),
- 'disableNotices' => true,
- )
- );
- i18n::register_translator($adapter, 'core');
- $adapter->removeCache();
- i18n::include_by_locale('en');
- }
-
- /**
- * Number of test manifests
- *
- * @var int
- */
- protected $manifests = 0;
-
- /**
- * Safely push a new class manifest.
- * These will be cleaned up on tearDown()
- *
- * @param ClassManifest $manifest
- */
- protected function pushManifest(ClassManifest $manifest)
- {
- $this->manifests++;
- ClassLoader::instance()->pushManifest($manifest);
+ $this->setupManifest();
}
public function tearDown()
{
- ThemeResourceLoader::set_instance($this->_oldLoader);
- i18n::set_locale($this->originalLocale);
- i18n::register_translator($this->origAdapter, 'core');
-
- while ($this->manifests > 0) {
- ClassLoader::instance()->popManifest();
- $this->manifests--;
- }
-
+ $this->tearDownManifest();
parent::tearDown();
}
@@ -145,16 +39,24 @@ public function testGetExistingTranslations()
public function testGetClosestTranslation()
{
-
// Validate necessary assumptions for this test
+ // As per set of locales loaded from _fakewebroot
$translations = i18n::get_existing_translations();
- $this->assertTrue(isset($translations['en_US']));
- $this->assertTrue(isset($translations['en_GB']));
- $this->assertTrue(isset($translations['es_ES']));
- $this->assertTrue(isset($translations['es_AR']));
- $this->assertFalse(isset($translations['en_ZZ']));
- $this->assertFalse(isset($translations['es_ZZ']));
- $this->assertFalse(isset($translations['zz_ZZ']));
+ $this->assertEquals(
+ [
+ 'en_GB',
+ 'en_US',
+ 'fr_FR',
+ 'de_AT',
+ 'de_DE',
+ 'ja_JP',
+ 'pl_PL',
+ 'es_AR',
+ 'es_ES',
+ 'mi_NZ',
+ ],
+ array_keys($translations)
+ );
// Test indeterminate locales
$this->assertEmpty(i18n::get_closest_translation('zz_ZZ'));
@@ -172,57 +74,51 @@ public function testGetClosestTranslation()
public function testDataObjectFieldLabels()
{
- $oldLocale = i18n::get_locale();
i18n::set_locale('de_DE');
- $obj = new i18nTest\TestDataObject();
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTest_DataObject.MyProperty' => 'MyProperty'
- ),
+ // Load into the translator as a literal array data source
+ /** @var SymfonyMessageProvider $provider */
+ $provider = Injector::inst()->get(MessageProvider::class);
+ $provider->getTranslator()->addResource(
+ 'array',
+ [ 'i18nTest_DataObject.MyProperty' => 'MyProperty' ],
'en_US'
);
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTest_DataObject.MyProperty' => 'Mein Attribut'
- ),
+ $provider->getTranslator()->addResource(
+ 'array',
+ [ 'i18nTest_DataObject.MyProperty' => 'Mein Attribut' ],
'de_DE'
);
+ $provider->getTranslator()->addResource(
+ 'array',
+ [ 'i18nTest_DataObject.MyUntranslatedProperty' => 'Mein Attribut' ],
+ 'en_US'
+ );
+ // Test field labels
+ $obj = new i18nTest\TestDataObject();
$this->assertEquals(
$obj->fieldLabel('MyProperty'),
'Mein Attribut'
);
-
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTest_DataObject.MyUntranslatedProperty' => 'Mein Attribut'
- ),
- 'en_US'
- );
$this->assertEquals(
$obj->fieldLabel('MyUntranslatedProperty'),
'My Untranslated Property'
);
-
- i18n::set_locale($oldLocale);
}
public function testProvideI18nEntities()
{
- $oldLocale = i18n::get_locale();
- i18n::set_locale('en_US');
-
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTest_Object.MyProperty' => 'Untranslated'
- ),
+ /** @var SymfonyMessageProvider $provider */
+ $provider = Injector::inst()->get(MessageProvider::class);
+ $provider->getTranslator()->addResource(
+ 'array',
+ [ 'i18nTest_Object.MyProperty' => 'Untranslated' ],
'en_US'
);
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTest_Object.my_translatable_property' => 'Übersetzt'
- ),
+ $provider->getTranslator()->addResource(
+ 'array',
+ [ 'i18nTest_Object.my_translatable_property' => 'Übersetzt' ],
'de_DE'
);
@@ -253,25 +149,30 @@ public function testProvideI18nEntities()
public function testTemplateTranslation()
{
$oldLocale = i18n::get_locale();
-
- i18n::set_locale('en_US');
- 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'
- ),
+ i18n::config()->update('missing_default_warning', false);
+
+ /** @var SymfonyMessageProvider $provider */
+ $provider = Injector::inst()->get(MessageProvider::class);
+ $provider->getTranslator()->addResource(
+ '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 = Convert::nl2os($viewer->process(new ArrayData(array('TestProperty' => 'TestPropertyValue'))));
+ $parsedHtml = Convert::nl2os($viewer->process(new ArrayData([
+ 'TestProperty' => 'TestPropertyValue'
+ ])));
$this->assertContains(
Convert::nl2os("Layout Template\n"),
$parsedHtml
@@ -281,22 +182,24 @@ public function testTemplateTranslation()
$parsedHtml
);
- i18n::set_locale('de_DE');
- 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'
- ),
+ $provider->getTranslator()->addResource(
+ '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',
+ 'i18nTestModule.PLURALS' => 'An item|{count} items',
+ ],
'de_DE'
);
+ i18n::set_locale('de_DE');
$viewer = new SSViewer('i18nTestModule');
$parsedHtml = Convert::nl2os($viewer->process(new ArrayData(array('TestProperty' => 'TestPropertyValue'))));
$this->assertContains(
@@ -331,35 +234,34 @@ public function testTemplateTranslation()
Convert::nl2os("TRANS My include replacement no namespace: TestPropertyValue\n"),
$parsedHtml
);
+ // Check plurals
+ $this->assertContains('Single: An item', $parsedHtml);
+ $this->assertContains('Multiple: 4 items', $parsedHtml);
+ $this->assertContains('None: 0 items', $parsedHtml);
i18n::set_locale($oldLocale);
}
public function testNewTMethodSignature()
{
- global $lang;
- $oldLocale = i18n::get_locale();
-
- i18n::set_locale('en_US');
-
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTestModule.NEWMETHODSIG' => 'TRANS New _t method signature test',
- 'i18nTestModule.INJECTIONS' => 'TRANS Hello {name} {greeting}. But it is late, {goodbye}',
- 'i18nTestModule.INJECTIONSLEGACY' => 'TRANS Hello %s %s. But it is late, %s',
- ),
+ /** @var SymfonyMessageProvider $provider */
+ $provider = Injector::inst()->get(MessageProvider::class);
+ $provider->getTranslator()->addResource(
+ 'array',
+ [
+ 'i18nTestModule.NEWMETHODSIG' => 'TRANS New _t method signature test',
+ 'i18nTestModule.INJECTIONS' => 'TRANS Hello {name} {greeting}. But it is late, {goodbye}',
+ 'i18nTestModule.INJECTIONSLEGACY' => 'TRANS Hello %s %s. But it is late, %s',
+ ],
'en_US'
);
$entity = "i18nTestModule.INJECTIONS";
$default = "Hello {name} {greeting}. But it is late, {goodbye}";
+ $entityLegacy = 'i18nTestModule.INJECTIONSLEGACY';
+ $defaultLegacy = 'TRANS Hello %s %s. But it is late, %s';
- $translated = i18n::_t('i18nTestModule.NEWMETHODSIG', "New _t method signature test");
- $this->assertContains(
- "TRANS New _t method signature test",
- $translated
- );
-
+ // Test missing entity key
$translated = i18n::_t(
$entity.'_DOES_NOT_EXIST',
$default,
@@ -371,10 +273,11 @@ public function testNewTMethodSignature()
"Testing fallback to the translation default (but using the injection array)"
);
+ // Test standard injection
$translated = i18n::_t(
$entity,
$default,
- array("name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you")
+ ["name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"]
);
$this->assertContains(
"TRANS Hello Paul good you are here. But it is late, see you",
@@ -382,11 +285,12 @@ public function testNewTMethodSignature()
"Testing entity, default string and injection array"
);
+ // @deprecated 5.0 Passing in context
$translated = i18n::_t(
$entity,
$default,
"New context (this should be ignored)",
- array("name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen")
+ ["name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"]
);
$this->assertContains(
"TRANS Hello Steffen willkommen. But it is late, wiedersehen",
@@ -394,16 +298,12 @@ public function testNewTMethodSignature()
"Full test of translation, using default, context and injection array"
);
- $translated = i18n::_t($entity, array("name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow"));
- $this->assertContains(
- "TRANS Hello Cat meow. But it is late, meow",
- $translated,
- "Testing a translation with just entity and injection array"
- );
-
+ // @deprecated 5.0 Passing in % placeholders (detected in default value)
+ // Note: Missing-placeholder substitution no longer functions
$translated = i18n::_t(
- 'i18nTestModule.INJECTIONSLEGACY', // has %s placeholders
- array("name"=>"Cat", "greeting2"=>"meow", "goodbye"=>"meow")
+ $entityLegacy, // has %s placeholders
+ $defaultLegacy,
+ ["name"=>"Cat", "greeting2"=>"meow", "goodbye"=>"meow"]
);
$this->assertContains(
"TRANS Hello Cat meow. But it is late, meow",
@@ -411,27 +311,13 @@ public function testNewTMethodSignature()
"Testing sprintf placeholders with named injections"
);
- $translated = i18n::_t(
- 'i18nTestModule.INJECTIONSLEGACY', // has %s placeholders
- array("Cat", "meow"/*, "meow" */) // remove third arg
- );
- $this->assertContains(
- "TRANS Hello Cat meow. But it is late, ",
- $translated,
- "Testing sprintf placeholders with unnamed injections and too few args"
- );
-
- $translated = i18n::_t(
- 'i18nTestModule.INJECTIONS', // has {name} placeholders
- array("Cat", "meow", "meow")
- );
- $this->assertContains(
- "TRANS Hello Cat meow. But it is late, meow",
- $translated,
- "Testing named injection placeholders with unnamed injections"
+ // Passing in non-associative arrays for placeholders is now an error
+ $this->setExpectedException(InvalidArgumentException::class, 'Injection must be an associative array');
+ i18n::_t(
+ $entity, // has {name} placeholders
+ $default,
+ ["Cat", "meow", "meow"]
);
-
- i18n::set_locale($oldLocale);
}
/**
@@ -439,20 +325,21 @@ public function testNewTMethodSignature()
* */
public function testNewTemplateTranslation()
{
- global $lang;
- $oldLocale = i18n::get_locale();
-
- i18n::set_locale('en_US');
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTestModule.NEWMETHODSIG' => 'TRANS New _t method signature test',
- 'i18nTestModule.INJECTIONS' => 'TRANS Hello {name} {greeting}. But it is late, {goodbye}'
- ),
+ i18n::config()->update('missing_default_warning', false);
+
+ /** @var SymfonyMessageProvider $provider */
+ $provider = Injector::inst()->get(MessageProvider::class);
+ $provider->getTranslator()->addResource(
+ 'array',
+ [
+ 'i18nTestModule.NEWMETHODSIG' => 'TRANS New _t method signature test',
+ 'i18nTestModule.INJECTIONS' => 'TRANS Hello {name} {greeting}. But it is late, {goodbye}'
+ ],
'en_US'
);
$viewer = new SSViewer('i18nTestModule');
- $parsedHtml = Convert::nl2os($viewer->process(new ArrayData(array('TestProperty' => 'TestPropertyValue'))));
+ $parsedHtml = Convert::nl2os($viewer->process(new ArrayData(['TestProperty' => 'TestPropertyValue'])));
$this->assertContains(
Convert::nl2os("Hello Mark welcome. But it is late, bye\n"),
$parsedHtml,
@@ -465,12 +352,6 @@ public function testNewTemplateTranslation()
"Testing entity, default string and injection array"
);
- $this->assertContains(
- Convert::nl2os("TRANS Hello Cat meow. But it is late, meow\n"),
- $parsedHtml,
- "Testing a translation with just entity and injection array"
- );
-
//test injected calls
$this->assertContains(
Convert::nl2os(
@@ -479,8 +360,6 @@ public function testNewTemplateTranslation()
$parsedHtml,
"Testing a translation with just entity and injection array, but with global variables injected in"
);
-
- i18n::set_locale($oldLocale);
}
public function testGetLocaleFromLang()
@@ -501,233 +380,101 @@ public function testValidateLocale()
public function testTranslate()
{
- $oldLocale = i18n::get_locale();
-
- i18n::get_translator('core')->getAdapter()->addTranslation(
- array(
- 'i18nTestModule.ENTITY' => 'Entity with "Double Quotes"',
- ),
+ /** @var SymfonyMessageProvider $provider */
+ $provider = Injector::inst()->get(MessageProvider::class);
+ $provider->getTranslator()->addResource(
+ '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)',
- ),
+ $provider->getTranslator()->addResource(
+ '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)',
- ),
+ $provider->getTranslator()->addResource(
+ 'array',
+ [
+ 'i18nTestModule.ENTITY' => 'Entity with "Double Quotes" (de_AT)',
+ ],
'de_AT'
);
$this->assertEquals(
- i18n::_t('i18nTestModule.ENTITY'),
'Entity with "Double Quotes"',
+ i18n::_t('i18nTestModule.ENTITY', 'Ignored default'),
'Returns translation in default language'
);
i18n::set_locale('de');
$this->assertEquals(
- i18n::_t('i18nTestModule.ENTITY'),
'Entity with "Double Quotes" (de)',
+ i18n::_t('i18nTestModule.ENTITY', 'Entity with "Double Quotes"'),
'Returns translation according to current locale'
);
i18n::set_locale('de_AT');
$this->assertEquals(
- i18n::_t('i18nTestModule.ENTITY'),
'Entity with "Double Quotes" (de_AT)',
+ i18n::_t('i18nTestModule.ENTITY', 'Entity with "Double Quotes"'),
'Returns specific regional translation if available'
);
$this->assertEquals(
- i18n::_t('i18nTestModule.ADDITION'),
'Addition (de)',
+ i18n::_t('i18nTestModule.ADDITION', 'Addition'),
'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);
- }
-
- public function testIncludeByLocale()
- {
- // Looping through modules, so we can test the translation autoloading
- // Load non-exclusive to retain core class autoloading
- $classManifest = new ClassManifest($this->alternateBasePath, true, true, false);
- $this->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'
- );
-
- // set _fakewebroot module priority
- i18n::config()->update('module_priority', array('subfolder','i18ntestmodule'));
-
- 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');
-
- // check module priority
- $this->assertEquals(
- $adapter->translate('i18nTestModule.PRIORITYNOTICE', 'de'),
- 'High Module Priority (de)'
- );
- }
-
- public function testIncludeByLocaleWithoutFallbackLanguage()
- {
- $classManifest = new ClassManifest($this->alternateBasePath, true, true, false);
- $this->pushManifest($classManifest);
-
- $adapter = i18n::get_translator('core')->getAdapter();
- $this->assertTrue($adapter->isAvailable('en'));
- $this->assertFalse($adapter->isAvailable('mi')); // not defined at all
- $this->assertFalse($adapter->isAvailable('mi_NZ')); // defined, but not loaded yet
- $this->assertFalse(
- $adapter->isTranslated('i18nTestModule.ENTITY', 'mi'),
- 'Existing unloaded entity not available before call'
- );
- $this->assertFalse(
- $adapter->isTranslated('i18nTestModule.ENTITY', 'mi_NZ'),
- 'Non-existing unloaded entity not available before call'
+ 'Entity with "Double Quotes" (fr)',
+ i18n::_t('i18nTestModule.ENTITY', 'Entity with "Double Quotes"'),
+ 'Non-specific locales fall back to language-only localisations'
);
-
- i18n::include_by_locale('mi_NZ');
-
- $this->assertFalse($adapter->isAvailable('mi'));
- $this->assertTrue($adapter->isAvailable('mi_NZ'));
- $this->assertTrue($adapter->isTranslated('i18nTestModule.ENTITY', null, 'mi_NZ'), 'Includes module files');
}
- public function testRegisterTranslator()
+ public function pluralisationDataProvider()
{
- $translator = new Zend_Translate(
- array(
- 'adapter' => CustomTranslatorAdapter::class,
- 'disableNotices' => true,
- )
- );
-
- i18n::register_translator($translator, 'custom', 10);
- $translators = i18n::get_translators();
- $this->assertArrayHasKey('custom', $translators[10]);
- $this->assertInstanceOf('Zend_Translate', $translators[10]['custom']);
- $this->assertInstanceOf(CustomTranslatorAdapter::class, $translators[10]['custom']->getAdapter());
-
- i18n::unregister_translator('custom');
- $translators = i18n::get_translators();
- $this->assertArrayNotHasKey('custom', $translators[10]);
+ return [
+ // English - 2 plural forms
+ ['en_NZ', 0, '0 months'],
+ ['en_NZ', 1, 'A month'],
+ ['en_NZ', 2, '2 months'],
+ ['en_NZ', 5, '5 months'],
+ ['en_NZ', 10, '10 months'],
+ // Polish - 4 plural forms
+ ['pl_PL', 0, '0 miesięcy'],
+ ['pl_PL', 1, '1 miesiąc'],
+ ['pl_PL', 2, '2 miesiące'],
+ ['pl_PL', 5, '5 miesięcy'],
+ ['pl_PL', 10, '10 miesięcy'],
+ // Japanese - 1 plural form
+ ['ja_JP', 0, '0日'],
+ ['ja_JP', 1, '1日'],
+ ['ja_JP', 2, '2日'],
+ ['ja_JP', 5, '5日'],
+ ['ja_JP', 10, '10日'],
+ ];
}
- public function testMultipleTranslators()
+ /**
+ * @dataProvider pluralisationDataProvider()
+ * @param string $locale
+ * @param int $count
+ * @param string $expected
+ */
+ public function testPluralisation($locale, $count, $expected)
{
- // Looping through modules, so we can test the translation autoloading
- // Load non-exclusive to retain core class autoloading
- $classManifest = new ClassManifest($this->alternateBasePath, true, true, false);
- $this->pushManifest($classManifest);
-
- // Changed manifest, so we also need to unset all previously collected messages.
- // The easiest way to do this it to register a new adapter.
- $adapter = new Zend_Translate(
- array(
- 'adapter' => i18nRailsYamlAdapter::class,
- 'locale' => i18n::config()->get('default_locale'),
- 'disableNotices' => true,
- )
- );
- i18n::register_translator($adapter, 'core');
-
- i18n::set_locale('en_US');
-
- $this->assertEquals(
- i18n::_t('i18nTestModule.ENTITY'),
- 'Entity with "Double Quotes"'
- );
+ i18n::set_locale($locale);
$this->assertEquals(
- i18n::_t('AdapterEntity1', 'AdapterEntity1'),
- 'AdapterEntity1',
- 'Falls back to default string if not found'
+ $expected,
+ _t('Month.PLURALS', 'A month|{count} months', ['count' => $count]),
+ "Plural form in locale $locale with count $count should be $expected"
);
-
- // Add a new translator
- $translator = new Zend_Translate(
- array(
- 'adapter' => CustomTranslatorAdapter::class,
- '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' => OtherCustomTranslatorAdapter::class,
- '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' => OtherCustomTranslatorAdapter::class,
- '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');
}
public function testGetLanguageName()
diff --git a/tests/php/i18n/i18nTest/CustomTranslatorAdapter.php b/tests/php/i18n/i18nTest/CustomTranslatorAdapter.php
deleted file mode 100644
index 7960403fa91..00000000000
--- a/tests/php/i18n/i18nTest/CustomTranslatorAdapter.php
+++ /dev/null
@@ -1,30 +0,0 @@
- array(
- 'AdapterEntity1' => 'AdapterEntity1 CustomAdapter (' . $locale . ')',
- 'i18nTestModule.ENTITY' => 'i18nTestModule.ENTITY CustomAdapter (' . $locale . ')',
- )
- );
- }
-
- public function toString()
- {
- return 'i18nTest_CustomTranslatorAdapter';
- }
-
- public function getFilenameForLocale($locale)
- {
- return false; // not file based
- }
-}
diff --git a/tests/php/i18n/i18nTest/MyObject.php b/tests/php/i18n/i18nTest/MyObject.php
index 790bf92ff99..2ccb995c577 100644
--- a/tests/php/i18n/i18nTest/MyObject.php
+++ b/tests/php/i18n/i18nTest/MyObject.php
@@ -2,6 +2,7 @@
namespace SilverStripe\i18n\Tests\i18nTest;
+use SilverStripe\Admin\LeftAndMain;
use SilverStripe\ORM\DataObject;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Security\Group;
@@ -22,4 +23,15 @@ class MyObject extends DataObject implements TestOnly
private static $singular_name = "My Object";
private static $plural_name = "My Objects";
+
+ public function provideI18nEntities()
+ {
+ $entities = parent::provideI18nEntities();
+ return array_merge($entities, [
+ LeftAndMain::class.'.OTHER_TITLE' => [
+ 'default' => 'Other title',
+ 'module' => 'admin',
+ ],
+ ]);
+ }
}
diff --git a/tests/php/i18n/i18nTest/OtherCustomTranslatorAdapter.php b/tests/php/i18n/i18nTest/OtherCustomTranslatorAdapter.php
deleted file mode 100644
index 68370d66568..00000000000
--- a/tests/php/i18n/i18nTest/OtherCustomTranslatorAdapter.php
+++ /dev/null
@@ -1,29 +0,0 @@
- array(
- 'i18nTestModule.ENTITY' => 'i18nTestModule.ENTITY OtherCustomAdapter (' . $locale . ')',
- )
- );
- }
-
- public function toString()
- {
- return 'i18nTest_OtherCustomTranslatorAdapter';
- }
-
- public function getFilenameForLocale($locale)
- {
- return false; // not file based
- }
-}
diff --git a/tests/php/i18n/i18nTest/TestDataObject.php b/tests/php/i18n/i18nTest/TestDataObject.php
index d6c0c10732a..41ceab1716d 100644
--- a/tests/php/i18n/i18nTest/TestDataObject.php
+++ b/tests/php/i18n/i18nTest/TestDataObject.php
@@ -8,6 +8,7 @@
class TestDataObject extends DataObject implements TestOnly
{
+ private static $table_name = 'i18nTest_TestDataObject';
private static $db = array(
'MyProperty' => 'Varchar',
diff --git a/tests/php/i18n/i18nTest/TestObject.php b/tests/php/i18n/i18nTest/TestObject.php
index 42412cf3143..51bc384ca35 100644
--- a/tests/php/i18n/i18nTest/TestObject.php
+++ b/tests/php/i18n/i18nTest/TestObject.php
@@ -2,11 +2,10 @@
namespace SilverStripe\i18n\Tests\i18nTest;
-use SilverStripe\Core\Object;
use SilverStripe\Dev\TestOnly;
use SilverStripe\i18n\i18nEntityProvider;
-class TestObject extends Object implements TestOnly, i18nEntityProvider
+class TestObject implements TestOnly, i18nEntityProvider
{
static $my_translatable_property = "Untranslated";
@@ -17,10 +16,8 @@ public static function my_translatable_property()
public function provideI18nEntities()
{
- return array(
- "i18nTest_Object.my_translatable_property" => array(
- self::$my_translatable_property
- )
- );
+ return [
+ "i18nTest_Object.my_translatable_property" => self::$my_translatable_property,
+ ];
}
}
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18nothermodule/code/i18nProviderClass.php b/tests/php/i18n/i18nTest/_fakewebroot/i18nothermodule/code/i18nProviderClass.php
new file mode 100644
index 00000000000..ef1bd581d77
--- /dev/null
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18nothermodule/code/i18nProviderClass.php
@@ -0,0 +1,23 @@
+ 'My Provider Class',
+ 'i18nProviderClass.PLURALS' => [
+ 'comment' => 'Plural forms for the test class',
+ 'one' => 'A class',
+ 'other' => '{count} classes',
+ ],
+ 'i18nProviderClass.OTHER_MODULE' => [
+ 'comment' => 'Test string in another module',
+ 'default' => 'i18ntestmodule string defined in i18nothermodule',
+ 'module' => 'i18ntestmodule'
+ ],
+ ];
+ }
+}
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/en.yml b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/en.yml
index b58eee65e33..f3ea1a6277b 100644
--- a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/en.yml
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/en.yml
@@ -11,5 +11,12 @@ en:
WITHNAMESPACE: Include Entity with Namespace
LAYOUTTEMPLATE: Layout Template
SPRINTFNAMESPACE: My replacement: %s
+ PLURALS:
+ one: 'A test'
+ other: '{count} tests'
i18nTestModuleInclude.ss:
SPRINTFINCLUDENAMESPACE: My include replacement: %s
+ Month:
+ PLURALS:
+ one: 'A month'
+ other: '{count} months'
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/en_GB.yml b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/en_GB.yml
new file mode 100644
index 00000000000..b58eee65e33
--- /dev/null
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/en_GB.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
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/es_AR.yml b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/es_AR.yml
new file mode 100644
index 00000000000..b58eee65e33
--- /dev/null
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/es_AR.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
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/es_ES.yml b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/es_ES.yml
new file mode 100644
index 00000000000..b58eee65e33
--- /dev/null
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/es_ES.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
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/ja.yml b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/ja.yml
new file mode 100644
index 00000000000..f778958a4dc
--- /dev/null
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/ja.yml
@@ -0,0 +1,4 @@
+ja:
+ Month:
+ PLURALS:
+ other: '{count}日'
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/pl.yml b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/pl.yml
new file mode 100644
index 00000000000..399de32ccac
--- /dev/null
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/lang/pl.yml
@@ -0,0 +1,7 @@
+pl:
+ Month:
+ PLURALS:
+ one: '1 miesiąc'
+ few: '{count} miesiące'
+ many: '{count} miesięcy'
+ other: '{count} miesiąca'
diff --git a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/templates/i18nTestModule.ss b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/templates/i18nTestModule.ss
index 57f99e453c5..e14ba28b81c 100644
--- a/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/templates/i18nTestModule.ss
+++ b/tests/php/i18n/i18nTest/_fakewebroot/i18ntestmodule/templates/i18nTestModule.ss
@@ -2,3 +2,6 @@
$Layout
lonely _t() call that should be ignored
<% _t('i18nTestModule.NEWENTITY',"Not stored in master file yet") %>
+Single: <%t i18nTestModule.PLURALS 'An item|{count} items' count=1 %>
+Multiple: <%t i18nTestModule.PLURALS 'An item|{count} items' count=4 %>
+None: <%t i18nTestModule.PLURALS 'An item|{count} items' count=0 %>
diff --git a/tests/php/i18n/i18nTestManifest.php b/tests/php/i18n/i18nTestManifest.php
new file mode 100644
index 00000000000..5c3756cb937
--- /dev/null
+++ b/tests/php/i18n/i18nTestManifest.php
@@ -0,0 +1,145 @@
+alternateBasePath = __DIR__ . $s . 'i18nTest' . $s . "_fakewebroot";
+ Director::config()->update('alternate_base_folder', $this->alternateBasePath);
+
+ // Replace old template loader with new one with alternate base path
+ $this->oldThemeResourceLoader = ThemeResourceLoader::instance();
+ ThemeResourceLoader::set_instance($loader = new ThemeResourceLoader($this->alternateBasePath));
+ $loader->addSet(
+ '$default',
+ new ThemeManifest(
+ $this->alternateBasePath,
+ project(),
+ false,
+ true
+ )
+ );
+
+ SSViewer::set_themes([
+ 'testtheme1',
+ '$default',
+ ]);
+
+ $this->originalLocale = i18n::get_locale();
+ i18n::set_locale('en_US');
+
+ // Set new manifest against the root
+ $classManifest = new ClassManifest($this->alternateBasePath, true, true, false);
+ $this->pushManifest($classManifest);
+
+ // Setup uncached translator
+ // This should pull the module list from the above manifest
+ $translator = new Translator('en');
+ $translator->setFallbackLocales(['en']);
+ $loader = new ModuleYamlLoader();
+ $loader->setReader(new YamlReader());
+ $translator->addLoader('ss', $loader); // Standard ss module loader
+ $translator->addLoader('array', new ArrayLoader()); // Note: array loader isn't added by default
+ $provider = new SymfonyMessageProvider();
+ $provider->setTranslator($translator);
+ Injector::inst()->registerService($provider, MessageProvider::class);
+ }
+
+ public function tearDownManifest()
+ {
+ ThemeResourceLoader::set_instance($this->oldThemeResourceLoader);
+ i18n::set_locale($this->originalLocale);
+
+ // Reset any manifests pushed during this test
+ $this->popManifests();
+ }
+
+ /**
+ * Safely push a new class manifest.
+ * These will be cleaned up on tearDown()
+ *
+ * @param ClassManifest $manifest
+ */
+ protected function pushManifest(ClassManifest $manifest)
+ {
+ $this->manifests++;
+ ClassLoader::instance()->pushManifest($manifest);
+ }
+
+ /**
+ * Pop off all extra manifests
+ */
+ protected function popManifests()
+ {
+ // Reset any manifests pushed during this test
+ while ($this->manifests > 0) {
+ ClassLoader::instance()->popManifest();
+ $this->manifests--;
+ }
+ }
+}
diff --git a/tests/php/i18n/i18nTextCollectorTest.php b/tests/php/i18n/i18nTextCollectorTest.php
index 903ab596015..1a25b2a4bdb 100644
--- a/tests/php/i18n/i18nTextCollectorTest.php
+++ b/tests/php/i18n/i18nTextCollectorTest.php
@@ -2,74 +2,46 @@
namespace SilverStripe\i18n\Tests;
+use PHPUnit_Framework_Error_Notice;
use SilverStripe\Assets\Filesystem;
-use SilverStripe\Control\Director;
-use SilverStripe\Core\Config\Config;
-use SilverStripe\Core\Convert;
-use SilverStripe\Core\Manifest\ClassManifest;
-use SilverStripe\Core\Manifest\ClassLoader;
-use SilverStripe\Dev\Debug;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\i18n\i18n;
-use SilverStripe\i18n\i18nTextCollector;
-use SilverStripe\i18n\i18nTextCollector_Writer_RailsYaml;
+use SilverStripe\i18n\TextCollection\i18nTextCollector;
+use SilverStripe\i18n\Messages\YamlWriter;
use SilverStripe\i18n\Tests\i18nTextCollectorTest\Collector;
-use SilverStripe\View\ThemeResourceLoader;
+use SilverStripe\View\SSViewer;
class i18nTextCollectorTest extends SapphireTest
{
+ use i18nTestManifest;
/**
- * @var string $tmpBasePath Used to write language files.
- * We don't want to store them inside framework (or in any web-accessible place)
- * in case something goes wrong with the file parsing.
+ * @var string
*/
- protected $alternateBaseSavePath;
-
- /**
- * @var string $alternateBasePath Fake webroot with a single module
- * /i18ntestmodule which contains some files with _t() calls.
- */
- protected $alternateBasePath;
-
- protected $manifest;
+ protected $alternateBaseSavePath = null;
public function setUp()
{
parent::setUp();
+ $this->setupManifest();
- $this->alternateBasePath = __DIR__ . "/i18nTest/_fakewebroot";
- Config::inst()->update(Director::class, 'alternate_base_folder', $this->alternateBasePath);
- $this->alternateBaseSavePath = TEMP_FOLDER . '/i18nTextCollectorTest_webroot';
+ $this->alternateBaseSavePath = TEMP_FOLDER . DIRECTORY_SEPARATOR . 'i18nTextCollectorTest_webroot';
Filesystem::makeFolder($this->alternateBaseSavePath);
-
- // Push a class and template loader running from the fake webroot onto
- // the stack.
- $this->manifest = new ClassManifest(
- $this->alternateBasePath,
- false,
- true,
- false
- );
-
- // Replace old template loader with new one with alternate base path
- $this->_oldLoader = ThemeResourceLoader::instance();
- ThemeResourceLoader::set_instance(new ThemeResourceLoader($this->alternateBasePath));
}
public function tearDown()
{
- ThemeResourceLoader::set_instance($this->_oldLoader);
- // Pop if added during testing
- if (ClassLoader::instance()->getManifest() === $this->manifest) {
- ClassLoader::instance()->popManifest();
+ if (is_dir($this->alternateBaseSavePath)) {
+ Filesystem::removeFolder($this->alternateBaseSavePath);
}
+
+ $this->tearDownManifest();
parent::tearDown();
}
public function testConcatenationInEntityValues()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
array(
- 'Test.CONCATENATED' => array("Line 1 and Line '2' and Line \"3\"",'Comment'),
- 'Test.CONCATENATED2' => array("Line \"4\" and Line 5")
- )
+ 'Test.CONCATENATED' => [
+ 'default' => "Line 1 and Line '2' and Line \"3\"",
+ 'comment' => 'Comment'
+ ],
+ 'Test.CONCATENATED2' => "Line \"4\" and Line 5"
+ ),
+ $c->collectFromCode($php, 'mymodule')
);
}
public function testCollectFromNewTemplateSyntaxUsingParserSubclass()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
+ $c->setWarnOnEmptyDefault(false);
- $html = <<
+ $html = <<
<%t i18nTestModule.NEWMETHODSIG "New _t method signature test" %>
<%t i18nTestModule.INJECTIONS_0 "Hello {name} {greeting}, and {goodbye}" name="Mark" greeting="welcome" goodbye="bye" %>
<%t i18nTestModule.INJECTIONS_1 "Hello {name} {greeting}, and {goodbye}" name="Paul" greeting="welcome" goodbye="cya" %>
<%t i18nTestModule.INJECTIONS_2 "Hello {name} {greeting}" is "context (ignored)" name="Steffen" greeting="Wilkommen" %>
<%t i18nTestModule.INJECTIONS_3 name="Cat" greeting='meow' goodbye="meow" %>
<%t i18nTestModule.INJECTIONS_4 name=\$absoluteBaseURL greeting=\$get_locale goodbye="global calls" %>
+<%t i18nTestModule.INJECTIONS_9 "An item|{count} items" is "Test Pluralisation" count=4 %>
SS;
$c->collectFromTemplate($html, 'mymodule', 'Test');
$this->assertEquals(
- $c->collectFromTemplate($html, 'mymodule', 'Test'),
- array(
- 'Test.SINGLEQUOTE' => array('Single Quote'),
- 'i18nTestModule.NEWMETHODSIG' => array("New _t method signature test",null,null),
- 'i18nTestModule.INJECTIONS_0' => array("Hello {name} {greeting}, and {goodbye}", null, null),
- 'i18nTestModule.INJECTIONS_1' => array("Hello {name} {greeting}, and {goodbye}", null, null),
- 'i18nTestModule.INJECTIONS_2' => array("Hello {name} {greeting}", null, "context (ignored)"),
- 'i18nTestModule.INJECTIONS_3' => array(null, null, null),
- 'i18nTestModule.INJECTIONS_4' => array(null, null, null),
- )
+ [
+ 'Test.SINGLEQUOTE' => 'Single Quote',
+ 'i18nTestModule.NEWMETHODSIG' => "New _t method signature test",
+ 'i18nTestModule.INJECTIONS_0' => "Hello {name} {greeting}, and {goodbye}",
+ 'i18nTestModule.INJECTIONS_1' => "Hello {name} {greeting}, and {goodbye}",
+ 'i18nTestModule.INJECTIONS_2' => [
+ 'default' => "Hello {name} {greeting}",
+ 'comment' => 'context (ignored)',
+ ],
+ 'i18nTestModule.INJECTIONS_9' => [
+ 'one' => 'An item',
+ 'other' => '{count} items',
+ 'comment' => 'Test Pluralisation'
+ ],
+ ],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
+ );
+
+ // Test warning is raised on empty default
+ $c->setWarnOnEmptyDefault(true);
+ $this->setExpectedException(
+ PHPUnit_Framework_Error_Notice::class,
+ 'Missing localisation default for key i18nTestModule.INJECTIONS_3'
);
+ $c->collectFromTemplate($html, 'mymodule', 'Test');
}
public function testCollectFromTemplateSimple()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
$html = <<
SS;
$this->assertEquals(
- $c->collectFromTemplate($html, 'mymodule', 'Test'),
- array(
- 'Test.SINGLEQUOTE' => array('Single Quote')
- )
+ [ 'Test.SINGLEQUOTE' => 'Single Quote' ],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
);
$html = <<
SS;
$this->assertEquals(
- $c->collectFromTemplate($html, 'mymodule', 'Test'),
- array(
- 'Test.DOUBLEQUOTE' => array("Double Quote and Spaces")
- )
+ [ 'Test.DOUBLEQUOTE' => "Double Quote and Spaces" ],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
);
$html = <<
SS;
$this->assertEquals(
- $c->collectFromTemplate($html, 'mymodule', 'Test'),
- array(
- 'Test.NOSEMICOLON' => array("No Semicolon")
- )
+ [ 'Test.NOSEMICOLON' => "No Semicolon" ],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
);
}
public function testCollectFromTemplateAdvanced()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
+ $c->setWarnOnEmptyDefault(false);
$html = <<
SS;
$this->assertEquals(
- $c->collectFromTemplate($html, 'mymodule', 'Test'),
- array(
- 'Test.NEWLINES' => array("New Lines")
- )
+ [ 'Test.NEWLINES' => "New Lines" ],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
);
$html = <<
SS;
$this->assertEquals(
- $c->collectFromTemplate($html, 'mymodule', 'Test'),
- array(
- 'Test.PRIOANDCOMMENT' => array(' Prio and Value with "Double Quotes"','Comment with "Double Quotes"')
- )
+ [ 'Test.PRIOANDCOMMENT' => [
+ 'default' => ' Prio and Value with "Double Quotes"',
+ 'comment' => 'Comment with "Double Quotes"',
+ ]],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
);
$html = <<
SS;
$this->assertEquals(
- $c->collectFromTemplate($html, 'mymodule', 'Test'),
- array(
- 'Test.PRIOANDCOMMENT' => array(" Prio and Value with 'Single Quotes'","Comment with 'Single Quotes'")
- )
+ [ 'Test.PRIOANDCOMMENT' => [
+ 'default' => " Prio and Value with 'Single Quotes'",
+ 'comment' => "Comment with 'Single Quotes'",
+ ]],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
);
+
+ // Test empty
+ $html = <<
+SS;
+ $this->assertEquals(
+ [],
+ $c->collectFromTemplate($html, 'mymodule', 'Test')
+ );
+
+ // Test warning is raised on empty default
+ $c->setWarnOnEmptyDefault(true);
+ $this->setExpectedException(
+ PHPUnit_Framework_Error_Notice::class,
+ 'Missing localisation default for key Test.PRIOANDCOMMENT'
+ );
+ $c->collectFromTemplate($html, 'mymodule', 'Test');
}
public function testCollectFromCodeSimple()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.SINGLEQUOTE' => array('Single Quote')
- )
+ [ 'Test.SINGLEQUOTE' => 'Single Quote' ],
+ $c->collectFromCode($php, 'mymodule')
);
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.DOUBLEQUOTE' => array("Double Quote and Spaces")
- )
+ [ 'Test.DOUBLEQUOTE' => "Double Quote and Spaces" ],
+ $c->collectFromCode($php, 'mymodule')
);
}
public function testCollectFromCodeAdvanced()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.NEWLINES' => array("New Lines")
- )
+ [ 'Test.NEWLINES' => "New Lines" ],
+ $c->collectFromCode($php, 'mymodule')
);
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.PRIOANDCOMMENT' => array(' Value with "Double Quotes"','Comment with "Double Quotes"')
- )
+ [
+ 'Test.PRIOANDCOMMENT' => [
+ 'default' => ' Value with "Double Quotes"',
+ 'comment' => 'Comment with "Double Quotes"',
+ ]
+ ],
+ $c->collectFromCode($php, 'mymodule')
);
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.PRIOANDCOMMENT' => array(" Value with 'Single Quotes'","Comment with 'Single Quotes'")
- )
+ [ 'Test.PRIOANDCOMMENT' => [
+ 'default' => " Value with 'Single Quotes'",
+ 'comment' => "Comment with 'Single Quotes'"
+ ] ],
+ $c->collectFromCode($php, 'mymodule')
);
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.PRIOANDCOMMENT' => array("Value with 'Escaped Single Quotes'")
- )
+ [ 'Test.PRIOANDCOMMENT' => "Value with 'Escaped Single Quotes'" ],
+ $c->collectFromCode($php, 'mymodule')
);
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.PRIOANDCOMMENT' => array("Doublequoted Value with 'Unescaped Single Quotes'")
- )
+ [ 'Test.PRIOANDCOMMENT' => "Doublequoted Value with 'Unescaped Single Quotes'"],
+ $c->collectFromCode($php, 'mymodule')
);
}
public function testNewlinesInEntityValues()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.NEWLINESINGLEQUOTE' => array("Line 1{$eol}Line 2")
- )
+ [ 'Test.NEWLINESINGLEQUOTE' => "Line 1{$eol}Line 2" ],
+ $c->collectFromCode($php, 'mymodule')
);
$php = <<assertEquals(
- $c->collectFromCode($php, 'mymodule'),
- array(
- 'Test.NEWLINEDOUBLEQUOTE' => array("Line 1{$eol}Line 2")
- )
+ [ 'Test.NEWLINEDOUBLEQUOTE' => "Line 1{$eol}Line 2" ],
+ $c->collectFromCode($php, 'mymodule')
);
}
@@ -347,79 +342,82 @@ public function testNewlinesInEntityValues()
*/
public function testCollectFromCodeNewSignature()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
+ $c->setWarnOnEmptyDefault(false); // Disable warnings for tests
$php = <<"Mark", "greeting"=>"welcome", "goodbye"=>"bye"));
_t('i18nTestModule.INJECTIONS2', "Hello {name} {greeting}. But it is late, {goodbye}",
array("name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"));
_t("i18nTestModule.INJECTIONS3", "Hello {name} {greeting}. But it is late, {goodbye}",
"New context (this should be ignored)",
array("name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"));
_t('i18nTestModule.INJECTIONS4', array("name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow"));
-_t('i18nTestModule.INJECTIONS5','_DOES_NOT_EXIST', "Hello {name} {greeting}. But it is late, {goodbye}",
- ["name"=>"Mark", "greeting"=>"welcome", "goodbye"=>"bye"]);
_t('i18nTestModule.INJECTIONS6', "Hello {name} {greeting}. But it is late, {goodbye}",
["name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"]);
_t("i18nTestModule.INJECTIONS7", "Hello {name} {greeting}. But it is late, {goodbye}",
"New context (this should be ignored)",
["name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"]);
_t('i18nTestModule.INJECTIONS8', ["name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow"]);
+_t('i18nTestModule.INJECTIONS9', "An item|{count} items", ['count' => 4], "Test Pluralisation");
PHP;
$collectedTranslatables = $c->collectFromCode($php, 'mymodule');
- $expectedArray = (array(
- 'i18nTestModule.NEWMETHODSIG' => array("New _t method signature test"),
- 'i18nTestModule.INJECTIONS1' => array("_DOES_NOT_EXIST",
- "Hello {name} {greeting}. But it is late, {goodbye}"),
- 'i18nTestModule.INJECTIONS2' => array("Hello {name} {greeting}. But it is late, {goodbye}"),
- 'i18nTestModule.INJECTIONS3' => array("Hello {name} {greeting}. But it is late, {goodbye}",
- "New context (this should be ignored)"),
- 'i18nTestModule.INJECTIONS5' => array("_DOES_NOT_EXIST",
- "Hello {name} {greeting}. But it is late, {goodbye}"),
- 'i18nTestModule.INJECTIONS6' => array("Hello {name} {greeting}. But it is late, {goodbye}"),
- 'i18nTestModule.INJECTIONS7' => array("Hello {name} {greeting}. But it is late, {goodbye}",
- "New context (this should be ignored)"),
- ));
-
- ksort($expectedArray);
-
- $this->assertEquals($collectedTranslatables, $expectedArray);
+ $expectedArray = [
+ 'i18nTestModule.INJECTIONS2' => "Hello {name} {greeting}. But it is late, {goodbye}",
+ 'i18nTestModule.INJECTIONS3' => [
+ 'default' => "Hello {name} {greeting}. But it is late, {goodbye}",
+ 'comment' => 'New context (this should be ignored)'
+ ],
+ 'i18nTestModule.INJECTIONS6' => "Hello {name} {greeting}. But it is late, {goodbye}",
+ 'i18nTestModule.INJECTIONS7' => [
+ 'default' => "Hello {name} {greeting}. But it is late, {goodbye}",
+ 'comment' => "New context (this should be ignored)",
+ ],
+ 'i18nTestModule.INJECTIONS9' => [
+ 'one' => 'An item',
+ 'other' => '{count} items',
+ 'comment' => 'Test Pluralisation',
+ ],
+ 'i18nTestModule.NEWMETHODSIG' => "New _t method signature test",
+ ];
+ $this->assertEquals($expectedArray, $collectedTranslatables);
+
+ // Test warning is raised on empty default
+ $this->setExpectedException(
+ PHPUnit_Framework_Error_Notice::class,
+ 'Missing localisation default for key i18nTestModule.INJECTIONS4'
+ );
+ $php = <<"Cat", "greeting"=>"meow", "goodbye"=>"meow"));
+PHP;
+ $c->setWarnOnEmptyDefault(true);
+ $c->collectFromCode($php, 'mymodule');
}
- /**
- * @todo Should be in a separate test suite, but don't want to duplicate setup logic
- */
- public function testYamlWriter()
+ public function testUncollectableCode()
{
- $writer = new i18nTextCollector_Writer_RailsYaml();
- $entities = array(
- 'Level1.Level2.EntityName' => array('Text', 'Context'),
- 'Level1.OtherEntityName' => array('Other Text', 'Other Context'),
- 'Level1.BoolTest' => array('True'),
- 'Level1.FlagTest' => array('No'),
- 'Level1.TextTest' => array('Maybe')
- );
- $yaml = <<assertEquals($yaml, Convert::nl2os($writer->getYaml($entities, 'de')));
+ $c = i18nTextCollector::create();
+
+ $php = <<collectFromCode($php, 'mymodule');
+
+ // Only one item is collectable
+ $expectedArray = [ 'Collectable.KEY4' => 'Default' ];
+ $this->assertEquals($expectedArray, $collectedTranslatables);
}
public function testCollectFromIncludedTemplates()
{
- $c = new i18nTextCollector();
+ $c = i18nTextCollector::create();
+ $c->setWarnOnEmptyDefault(false); // Disable warnings for tests
$templateFilePath = $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss';
$html = file_get_contents($templateFilePath);
@@ -427,23 +425,23 @@ public function testCollectFromIncludedTemplates()
$this->assertArrayHasKey('RandomNamespace.LAYOUTTEMPLATENONAMESPACE', $matches);
$this->assertEquals(
- $matches['RandomNamespace.LAYOUTTEMPLATENONAMESPACE'],
- array('Layout Template no namespace')
+ 'Layout Template no namespace',
+ $matches['RandomNamespace.LAYOUTTEMPLATENONAMESPACE']
);
$this->assertArrayHasKey('RandomNamespace.SPRINTFNONAMESPACE', $matches);
$this->assertEquals(
- $matches['RandomNamespace.SPRINTFNONAMESPACE'],
- array('My replacement no namespace: %s')
+ 'My replacement no namespace: %s',
+ $matches['RandomNamespace.SPRINTFNONAMESPACE']
);
$this->assertArrayHasKey('i18nTestModule.LAYOUTTEMPLATE', $matches);
$this->assertEquals(
- $matches['i18nTestModule.LAYOUTTEMPLATE'],
- array('Layout Template')
+ 'Layout Template',
+ $matches['i18nTestModule.LAYOUTTEMPLATE']
);
$this->assertArrayHasKey('i18nTestModule.SPRINTFNAMESPACE', $matches);
$this->assertEquals(
- $matches['i18nTestModule.SPRINTFNAMESPACE'],
- array('My replacement: %s')
+ 'My replacement: %s',
+ $matches['i18nTestModule.SPRINTFNAMESPACE']
);
// Includes should not automatically inject translations into parent templates
@@ -455,8 +453,8 @@ public function testCollectFromIncludedTemplates()
public function testCollectFromThemesTemplates()
{
- $c = new i18nTextCollector();
- Config::inst()->update('SilverStripe\\View\\SSViewer', 'theme', 'testtheme1');
+ $c = i18nTextCollector::create();
+ SSViewer::set_themes([ 'testtheme1' ]);
// Collect from layout
$layoutFilePath = $this->alternateBasePath . '/themes/testtheme1/templates/Layout/i18nTestTheme1.ss';
@@ -465,16 +463,12 @@ public function testCollectFromThemesTemplates()
// all entities from i18nTestTheme1.ss
$this->assertEquals(
- array(
- 'i18nTestTheme1.LAYOUTTEMPLATE'
- => array('Theme1 Layout Template'),
- 'i18nTestTheme1.SPRINTFNAMESPACE'
- => array('Theme1 My replacement: %s'),
- 'i18nTestTheme1.ss.LAYOUTTEMPLATENONAMESPACE'
- => array('Theme1 Layout Template no namespace'),
- 'i18nTestTheme1.ss.SPRINTFNONAMESPACE'
- => array('Theme1 My replacement no namespace: %s'),
- ),
+ [
+ 'i18nTestTheme1.LAYOUTTEMPLATE' => 'Theme1 Layout Template',
+ 'i18nTestTheme1.SPRINTFNAMESPACE' => 'Theme1 My replacement: %s',
+ 'i18nTestTheme1.ss.LAYOUTTEMPLATENONAMESPACE' => 'Theme1 Layout Template no namespace',
+ 'i18nTestTheme1.ss.SPRINTFNONAMESPACE' => 'Theme1 My replacement no namespace: %s',
+ ],
$layoutMatches
);
@@ -485,29 +479,21 @@ public function testCollectFromThemesTemplates()
// all entities from i18nTestTheme1Include.ss
$this->assertEquals(
- array(
- 'i18nTestTheme1Include.SPRINTFINCLUDENAMESPACE'
- => array('Theme1 My include replacement: %s'),
- 'i18nTestTheme1Include.WITHNAMESPACE'
- => array('Theme1 Include Entity with Namespace'),
- 'i18nTestTheme1Include.ss.NONAMESPACE'
- => array('Theme1 Include Entity without Namespace'),
- 'i18nTestTheme1Include.ss.SPRINTFINCLUDENONAMESPACE'
- => array('Theme1 My include replacement no namespace: %s')
- ),
+ [
+ 'i18nTestTheme1Include.SPRINTFINCLUDENAMESPACE' => 'Theme1 My include replacement: %s',
+ 'i18nTestTheme1Include.WITHNAMESPACE' => 'Theme1 Include Entity with Namespace',
+ 'i18nTestTheme1Include.ss.NONAMESPACE' => 'Theme1 Include Entity without Namespace',
+ 'i18nTestTheme1Include.ss.SPRINTFINCLUDENONAMESPACE' => 'Theme1 My include replacement no namespace: %s'
+ ],
$includeMatches
);
}
public function testCollectMergesWithExisting()
{
- i18n::set_locale('en_US');
- i18n::config()->update('default_locale', 'en_US');
- i18n::include_by_locale('en');
- i18n::include_by_locale('en_US');
-
- $c = new i18nTextCollector();
- $c->setWriter(new i18nTextCollector_Writer_RailsYaml());
+ $c = i18nTextCollector::create();
+ $c->setWarnOnEmptyDefault(false);
+ $c->setWriter(new YamlWriter());
$c->basePath = $this->alternateBasePath;
$c->baseSavePath = $this->alternateBaseSavePath;
@@ -522,16 +508,30 @@ public function testCollectMergesWithExisting()
$entitiesByModule['i18ntestmodule'],
'Adds new entities'
);
+
+ // Test cross-module strings are set correctly
+ $this->assertArrayHasKey(
+ 'i18nProviderClass.OTHER_MODULE',
+ $entitiesByModule['i18ntestmodule']
+ );
+ $this->assertEquals(
+ [
+ 'comment' => 'Test string in another module',
+ 'default' => 'i18ntestmodule string defined in i18nothermodule',
+ ],
+ $entitiesByModule['i18ntestmodule']['i18nProviderClass.OTHER_MODULE']
+ );
}
public function testCollectFromFilesystemAndWriteMasterTables()
{
- $local = i18n::get_locale();
i18n::set_locale('en_US'); //set the locale to the US locale expected in the asserts
i18n::config()->update('default_locale', 'en_US');
+ i18n::config()->update('missing_default_warning', false);
- $c = new i18nTextCollector();
- $c->setWriter(new i18nTextCollector_Writer_RailsYaml());
+ $c = i18nTextCollector::create();
+ $c->setWarnOnEmptyDefault(false);
+ $c->setWriter(new YamlWriter());
$c->basePath = $this->alternateBasePath;
$c->baseSavePath = $this->alternateBaseSavePath;
@@ -642,26 +642,62 @@ public function testCollectFromFilesystemAndWriteMasterTables()
" MAINTEMPLATE: 'Theme2 Main Template'\n",
$theme2LangFileContent
);
-
- i18n::set_locale($local); //set the locale to the US locale expected in the asserts
}
public function testCollectFromEntityProvidersInCustomObject()
{
- $c = new i18nTextCollector();
+ // note: Disable _fakewebroot manifest for this test
+ $this->popManifests();
+
+ $c = i18nTextCollector::create();
+ // Collect from MyObject.php
$filePath = __DIR__ . '/i18nTest/MyObject.php';
$matches = $c->collectFromEntityProviders($filePath);
$this->assertEquals(
- array(
- 'SilverStripe\i18n\Tests\i18nTest\MyObject.PLURALNAME',
- 'SilverStripe\i18n\Tests\i18nTest\MyObject.SINGULARNAME',
- ),
- array_keys($matches)
+ [
+ 'SilverStripe\Admin\LeftAndMain.OTHER_TITLE' => [
+ 'default' => 'Other title',
+ 'module' => 'admin',
+ ],
+ 'SilverStripe\i18n\Tests\i18nTest\MyObject.PLURALNAME' => 'My Objects',
+ 'SilverStripe\i18n\Tests\i18nTest\MyObject.PLURALS' => [
+ 'one' => 'A My Object',
+ 'other' => '{count} My Objects',
+ ],
+ 'SilverStripe\i18n\Tests\i18nTest\MyObject.SINGULARNAME' => 'My Object',
+ ],
+ $matches
);
+ }
+
+ public function testCollectFromEntityProvidersInWebRoot()
+ {
+ // Collect from i18nProviderClass
+ $c = i18nTextCollector::create();
+ $c->setWarnOnEmptyDefault(false);
+ $c->setWriter(new YamlWriter());
+ $c->basePath = $this->alternateBasePath;
+ $c->baseSavePath = $this->alternateBaseSavePath;
+ $entitiesByModule = $c->collect(null, false);
$this->assertEquals(
- 'My Object',
- $matches['SilverStripe\i18n\Tests\i18nTest\MyObject.SINGULARNAME'][0]
+ [
+ 'comment' => 'Plural forms for the test class',
+ 'one' => 'A class',
+ 'other' => '{count} classes',
+ ],
+ $entitiesByModule['i18nothermodule']['i18nProviderClass.PLURALS']
+ );
+ $this->assertEquals(
+ 'My Provider Class',
+ $entitiesByModule['i18nothermodule']['i18nProviderClass.TITLE']
+ );
+ $this->assertEquals(
+ [
+ 'comment' => 'Test string in another module',
+ 'default' => 'i18ntestmodule string defined in i18nothermodule',
+ ],
+ $entitiesByModule['i18ntestmodule']['i18nProviderClass.OTHER_MODULE']
);
}
@@ -671,48 +707,47 @@ public function testCollectFromEntityProvidersInCustomObject()
public function testResolveDuplicates()
{
$collector = new Collector();
- ClassLoader::instance()->pushManifest($this->manifest);
// Dummy data as collected
- $data1 = array(
- 'i18ntestmodule' => array(
- 'i18nTestModule.PLURALNAME' => array('Data Objects'),
- 'i18nTestModule.SINGULARNAME' => array('Data Object')
- ),
- 'mymodule' => array(
- 'i18nTestModule.PLURALNAME' => array('Ignored String'),
- 'i18nTestModule.STREETNAME' => array('Shortland Street')
- )
- );
- $expected = array(
- 'i18ntestmodule' => array(
- 'i18nTestModule.PLURALNAME' => array('Data Objects'),
- 'i18nTestModule.SINGULARNAME' => array('Data Object')
- ),
- 'mymodule' => array(
- // Because this key doesn't exist in i18ntestmodule strings
- 'i18nTestModule.STREETNAME' => array('Shortland Street')
- )
- );
+ $data1 = [
+ 'i18ntestmodule' => [
+ 'i18nTestModule.PLURALNAME' => 'Data Objects',
+ 'i18nTestModule.SINGULARNAME' => 'Data Object',
+ ],
+ 'mymodule' => [
+ 'i18nTestModule.PLURALNAME' => 'Ignored String',
+ 'i18nTestModule.STREETNAME' => 'Shortland Street',
+ ],
+ ];
+ $expected = [
+ 'i18ntestmodule' => [
+ 'i18nTestModule.PLURALNAME' => 'Data Objects',
+ 'i18nTestModule.SINGULARNAME' => 'Data Object',
+ ],
+ 'mymodule' => [
+ // Removed PLURALNAME because this key doesn't exist in i18ntestmodule strings
+ 'i18nTestModule.STREETNAME' => 'Shortland Street'
+ ]
+ ];
$resolved = $collector->resolveDuplicateConflicts_Test($data1);
$this->assertEquals($expected, $resolved);
// Test getConflicts
- $data2 = array(
- 'module1' => array(
- 'i18ntestmodule.ONE' => array('One'),
- 'i18ntestmodule.TWO' => array('Two'),
- 'i18ntestmodule.THREE' => array('Three'),
- ),
- 'module2' => array(
- 'i18ntestmodule.THREE' => array('Three'),
- ),
- 'module3' => array(
- 'i18ntestmodule.TWO' => array('Two'),
- 'i18ntestmodule.THREE' => array('Three'),
- )
- );
+ $data2 = [
+ 'module1' => [
+ 'i18ntestmodule.ONE' => 'One',
+ 'i18ntestmodule.TWO' => 'Two',
+ 'i18ntestmodule.THREE' => 'Three',
+ ],
+ 'module2' => [
+ 'i18ntestmodule.THREE' => 'Three',
+ ],
+ 'module3' => [
+ 'i18ntestmodule.TWO' => 'Two',
+ 'i18ntestmodule.THREE' => 'Three',
+ ],
+ ];
$conflictsA = $collector->getConflicts_Test($data2);
sort($conflictsA);
$this->assertEquals(
@@ -735,7 +770,6 @@ public function testResolveDuplicates()
public function testModuleDetection()
{
$collector = new Collector();
- ClassLoader::instance()->pushManifest($this->manifest);
$modules = $collector->getModules_Test($this->alternateBasePath);
$this->assertEquals(
array(
@@ -790,9 +824,10 @@ public function testModuleFileList()
// Standard modules with code in odd places should only have code in those directories detected
$otherFiles = $collector->getFileListForModule_Test('i18nothermodule');
$otherRoot = $this->alternateBasePath . '/i18nothermodule';
- $this->assertEquals(3, count($otherFiles));
+ $this->assertEquals(4, count($otherFiles));
// Only detect well-behaved files
$this->assertArrayHasKey("{$otherRoot}/code/i18nOtherModule.php", $otherFiles);
+ $this->assertArrayHasKey("{$otherRoot}/code/i18nProviderClass.php", $otherFiles);
$this->assertArrayHasKey("{$otherRoot}/code/i18nTestModuleDecorator.php", $otherFiles);
$this->assertArrayHasKey("{$otherRoot}/templates/i18nOtherModule.ss", $otherFiles);
diff --git a/tests/php/i18n/i18nTextCollectorTest/Collector.php b/tests/php/i18n/i18nTextCollectorTest/Collector.php
index 9c5c7629b35..cd33be41409 100644
--- a/tests/php/i18n/i18nTextCollectorTest/Collector.php
+++ b/tests/php/i18n/i18nTextCollectorTest/Collector.php
@@ -3,7 +3,7 @@
namespace SilverStripe\i18n\Tests\i18nTextCollectorTest;
use SilverStripe\Dev\TestOnly;
-use SilverStripe\i18n\i18nTextCollector;
+use SilverStripe\i18n\TextCollection\i18nTextCollector;
/**
* Assist with testing of specific protected methods
diff --git a/thirdparty/Zend/Translate.php b/thirdparty/Zend/Translate.php
deleted file mode 100644
index 7a5d4c3c8b4..00000000000
--- a/thirdparty/Zend/Translate.php
+++ /dev/null
@@ -1,220 +0,0 @@
-toArray();
- } else if (func_num_args() > 1) {
- $args = func_get_args();
- $options = array();
- $options['adapter'] = array_shift($args);
- if (!empty($args)) {
- $options['content'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $options['locale'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $opt = array_shift($args);
- $options = array_merge($opt, $options);
- }
- } else if (!is_array($options)) {
- $options = array('adapter' => $options);
- }
-
- $this->setAdapter($options);
- }
-
- /**
- * Sets a new adapter
- *
- * @param array|Zend_Config $options Options to use
- * @throws Zend_Translate_Exception
- */
- public function setAdapter($options = array())
- {
- if ($options instanceof Zend_Config) {
- $options = $options->toArray();
- } else if (func_num_args() > 1) {
- $args = func_get_args();
- $options = array();
- $options['adapter'] = array_shift($args);
- if (!empty($args)) {
- $options['content'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $options['locale'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $opt = array_shift($args);
- $options = array_merge($opt, $options);
- }
- } else if (!is_array($options)) {
- $options = array('adapter' => $options);
- }
-
- if (Zend_Loader::isReadable('Zend/Translate/Adapter/' . ucfirst($options['adapter']). '.php')) {
- $options['adapter'] = 'Zend_Translate_Adapter_' . ucfirst($options['adapter']);
- }
-
- if (!class_exists($options['adapter'])) {
- Zend_Loader::loadClass($options['adapter']);
- }
-
- if (array_key_exists('cache', $options)) {
- Zend_Translate_Adapter::setCache($options['cache']);
- }
-
- $adapter = $options['adapter'];
- unset($options['adapter']);
- $this->_adapter = new $adapter($options);
- if (!$this->_adapter instanceof Zend_Translate_Adapter) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("Adapter " . $adapter . " does not extend Zend_Translate_Adapter");
- }
- }
-
- /**
- * Returns the adapters name and it's options
- *
- * @return Zend_Translate_Adapter
- */
- public function getAdapter()
- {
- return $this->_adapter;
- }
-
- /**
- * Returns the set cache
- *
- * @return Zend_Cache_Core The set cache
- */
- public static function getCache()
- {
- return Zend_Translate_Adapter::getCache();
- }
-
- /**
- * Sets a cache for all instances of Zend_Translate
- *
- * @param Zend_Cache_Core $cache Cache to store to
- * @return void
- */
- public static function setCache(Zend_Cache_Core $cache)
- {
- Zend_Translate_Adapter::setCache($cache);
- }
-
- /**
- * Returns true when a cache is set
- *
- * @return boolean
- */
- public static function hasCache()
- {
- return Zend_Translate_Adapter::hasCache();
- }
-
- /**
- * Removes any set cache
- *
- * @return void
- */
- public static function removeCache()
- {
- Zend_Translate_Adapter::removeCache();
- }
-
- /**
- * Clears all set cache data
- *
- * @param string $tag Tag to clear when the default tag name is not used
- * @return void
- */
- public static function clearCache($tag = null)
- {
- Zend_Translate_Adapter::clearCache($tag);
- }
-
- /**
- * Calls all methods from the adapter
- */
- public function __call($method, array $options)
- {
- if (method_exists($this->_adapter, $method)) {
- return call_user_func_array(array($this->_adapter, $method), $options);
- }
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("Unknown method '" . $method . "' called!");
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter.php b/thirdparty/Zend/Translate/Adapter.php
deleted file mode 100644
index 38fb15f43fc..00000000000
--- a/thirdparty/Zend/Translate/Adapter.php
+++ /dev/null
@@ -1,998 +0,0 @@
- when true, clears already loaded translations when adding new files
- * 'content' => content to translate or file or directory with content
- * 'disableNotices' => when true, omits notices from being displayed
- * 'ignore' => a prefix for files and directories which are not being added
- * 'locale' => the actual set locale to use
- * 'log' => a instance of Zend_Log where logs are written to
- * 'logMessage' => message to be logged
- * 'logPriority' => priority which is used to write the log message
- * 'logUntranslated' => when true, untranslated messages are not logged
- * 'reload' => reloads the cache by reading the content again
- * 'scan' => searches for translation files using the LOCALE constants
- * 'tag' => tag to use for the cache
- *
- * @var array
- */
- protected $_options = array(
- 'clear' => false,
- 'content' => null,
- 'disableNotices' => false,
- 'ignore' => '.',
- 'locale' => 'auto',
- 'log' => null,
- 'logMessage' => "Untranslated message within '%locale%': %message%",
- 'logPriority' => 5,
- 'logUntranslated' => false,
- 'reload' => false,
- 'route' => null,
- 'scan' => null,
- 'tag' => 'Zend_Translate'
- );
-
- /**
- * Translation table
- * @var array
- */
- protected $_translate = array();
-
- /**
- * Generates the adapter
- *
- * @param array|Zend_Config $options Translation options for this adapter
- * @throws Zend_Translate_Exception
- * @return void
- */
- public function __construct($options = array())
- {
- if ($options instanceof Zend_Config) {
- $options = $options->toArray();
- } else if (func_num_args() > 1) {
- $args = func_get_args();
- $options = array();
- $options['content'] = array_shift($args);
-
- if (!empty($args)) {
- $options['locale'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $opt = array_shift($args);
- $options = array_merge($opt, $options);
- }
- } else if (!is_array($options)) {
- $options = array('content' => $options);
- }
-
- if (array_key_exists('cache', $options)) {
- self::setCache($options['cache']);
- unset($options['cache']);
- }
-
- if (isset(self::$_cache)) {
- $id = 'Zend_Translate_' . $this->toString() . '_Options';
- $result = self::$_cache->load($id);
- if ($result) {
- $this->_options = $result;
- }
- }
-
- if (empty($options['locale']) || ($options['locale'] === "auto")) {
- $this->_automatic = true;
- } else {
- $this->_automatic = false;
- }
-
- $locale = null;
- if (!empty($options['locale'])) {
- $locale = $options['locale'];
- unset($options['locale']);
- }
-
- $this->setOptions($options);
- $options['locale'] = $locale;
-
- if (!empty($options['content'])) {
- $this->addTranslation($options);
- }
-
- if ($this->getLocale() !== (string) $options['locale']) {
- $this->setLocale($options['locale']);
- }
- }
-
- /**
- * Add translations
- *
- * This may be a new language or additional content for an existing language
- * If the key 'clear' is true, then translations for the specified
- * language will be replaced and added otherwise
- *
- * @param array|Zend_Config $options Options and translations to be added
- * @throws Zend_Translate_Exception
- * @return Zend_Translate_Adapter Provides fluent interface
- */
- public function addTranslation($options = array())
- {
- if ($options instanceof Zend_Config) {
- $options = $options->toArray();
- } else if (func_num_args() > 1) {
- $args = func_get_args();
- $options = array();
- $options['content'] = array_shift($args);
-
- if (!empty($args)) {
- $options['locale'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $opt = array_shift($args);
- $options = array_merge($opt, $options);
- }
- } else if (!is_array($options)) {
- $options = array('content' => $options);
- }
-
- if (!isset($options['content']) || empty($options['content'])) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("Required option 'content' is missing");
- }
-
- $originate = null;
- if (!empty($options['locale'])) {
- $originate = (string) $options['locale'];
- }
-
- if ((array_key_exists('log', $options)) && !($options['log'] instanceof Zend_Log)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
- }
-
- try {
- if (!($options['content'] instanceof Zend_Translate) && !($options['content'] instanceof Zend_Translate_Adapter)) {
- if (empty($options['locale'])) {
- $options['locale'] = null;
- }
-
- $options['locale'] = Zend_Locale::findLocale($options['locale']);
- }
- } catch (Zend_Locale_Exception $e) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
- }
-
- $options = $options + $this->_options;
- if (is_string($options['content']) and is_dir($options['content'])) {
- $options['content'] = realpath($options['content']);
- $prev = '';
- $iterator = new RecursiveIteratorIterator(
- new RecursiveRegexIterator(
- new RecursiveDirectoryIterator($options['content'], RecursiveDirectoryIterator::KEY_AS_PATHNAME),
- '/^(?!.*(\.svn|\.cvs)).*$/', RecursiveRegexIterator::MATCH
- ),
- RecursiveIteratorIterator::SELF_FIRST
- );
-
- foreach ($iterator as $directory => $info) {
- $file = $info->getFilename();
- if (is_array($options['ignore'])) {
- foreach ($options['ignore'] as $key => $ignore) {
- if (strpos($key, 'regex') !== false) {
- if (preg_match($ignore, $directory)) {
- // ignore files matching the given regex from option 'ignore' and all files below
- continue 2;
- }
- } else if (strpos($directory, DIRECTORY_SEPARATOR . $ignore) !== false) {
- // ignore files matching first characters from option 'ignore' and all files below
- continue 2;
- }
- }
- } else {
- if (strpos($directory, DIRECTORY_SEPARATOR . $options['ignore']) !== false) {
- // ignore files matching first characters from option 'ignore' and all files below
- continue;
- }
- }
-
- if ($info->isDir()) {
- // pathname as locale
- if (($options['scan'] === self::LOCALE_DIRECTORY) and (Zend_Locale::isLocale($file, true, false))) {
- $options['locale'] = $file;
- $prev = (string) $options['locale'];
- }
- } else if ($info->isFile()) {
- // filename as locale
- if ($options['scan'] === self::LOCALE_FILENAME) {
- $filename = explode('.', $file);
- array_pop($filename);
- $filename = implode('.', $filename);
- if (Zend_Locale::isLocale((string) $filename, true, false)) {
- $options['locale'] = (string) $filename;
- } else {
- $parts = explode('.', $file);
- $parts2 = array();
- foreach($parts as $token) {
- $parts2 += explode('_', $token);
- }
- $parts = array_merge($parts, $parts2);
- $parts2 = array();
- foreach($parts as $token) {
- $parts2 += explode('-', $token);
- }
- $parts = array_merge($parts, $parts2);
- $parts = array_unique($parts);
- $prev = '';
- foreach($parts as $token) {
- if (Zend_Locale::isLocale($token, true, false)) {
- if (strlen($prev) <= strlen($token)) {
- $options['locale'] = $token;
- $prev = $token;
- }
- }
- }
- }
- }
-
- try {
- $options['content'] = $info->getPathname();
- $this->_addTranslationData($options);
- } catch (Zend_Translate_Exception $e) {
- // ignore failed sources while scanning
- }
- }
- }
-
- unset($iterator);
- } else {
- $this->_addTranslationData($options);
- }
-
- if ((isset($this->_translate[$originate]) === true) and (count($this->_translate[$originate]) > 0)) {
- $this->setLocale($originate);
- }
-
- return $this;
- }
-
- /**
- * Sets new adapter options
- *
- * @param array $options Adapter options
- * @throws Zend_Translate_Exception
- * @return Zend_Translate_Adapter Provides fluent interface
- */
- public function setOptions(array $options = array())
- {
- $change = false;
- $locale = null;
- foreach ($options as $key => $option) {
- if ($key == 'locale') {
- $locale = $option;
- } else if ((isset($this->_options[$key]) and ($this->_options[$key] != $option)) or
- !isset($this->_options[$key])) {
- if (($key == 'log') && !($option instanceof Zend_Log)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
- }
-
- if ($key == 'cache') {
- self::setCache($option);
- continue;
- }
-
- $this->_options[$key] = $option;
- $change = true;
- }
- }
-
- if ($locale !== null) {
- $this->setLocale($locale);
- }
-
- if (isset(self::$_cache) and ($change == true)) {
- $id = 'Zend_Translate_' . $this->toString() . '_Options';
- if (self::$_cacheTags) {
- self::$_cache->save($this->_options, $id, array($this->_options['tag']));
- } else {
- self::$_cache->save($this->_options, $id);
- }
- }
-
- return $this;
- }
-
- /**
- * Returns the adapters name and it's options
- *
- * @param string|null $optionKey String returns this option
- * null returns all options
- * @return integer|string|array|null
- */
- public function getOptions($optionKey = null)
- {
- if ($optionKey === null) {
- return $this->_options;
- }
-
- if (isset($this->_options[$optionKey]) === true) {
- return $this->_options[$optionKey];
- }
-
- return null;
- }
-
- /**
- * Gets locale
- *
- * @return Zend_Locale|string|null
- */
- public function getLocale()
- {
- return $this->_options['locale'];
- }
-
- /**
- * Sets locale
- *
- * @param string|Zend_Locale $locale Locale to set
- * @throws Zend_Translate_Exception
- * @return Zend_Translate_Adapter Provides fluent interface
- */
- public function setLocale($locale)
- {
- if (($locale === "auto") or ($locale === null)) {
- $this->_automatic = true;
- } else {
- $this->_automatic = false;
- }
-
- try {
- $locale = Zend_Locale::findLocale($locale);
- } catch (Zend_Locale_Exception $e) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("The given Language ({$locale}) does not exist", 0, $e);
- }
-
- if (!isset($this->_translate[$locale])) {
- $temp = explode('_', $locale);
- if (!isset($this->_translate[$temp[0]]) and !isset($this->_translate[$locale])) {
- if (!$this->_options['disableNotices']) {
- if ($this->_options['log']) {
- $this->_options['log']->log("The language '{$locale}' has to be added before it can be used.", $this->_options['logPriority']);
- } else {
- trigger_error("The language '{$locale}' has to be added before it can be used.", E_USER_NOTICE);
- }
- }
- }
-
- $locale = $temp[0];
- }
-
- if (empty($this->_translate[$locale])) {
- if (!$this->_options['disableNotices']) {
- if ($this->_options['log']) {
- $this->_options['log']->log("No translation for the language '{$locale}' available.", $this->_options['logPriority']);
- } else {
- trigger_error("No translation for the language '{$locale}' available.", E_USER_NOTICE);
- }
- }
- }
-
- if ($this->_options['locale'] != $locale) {
- $this->_options['locale'] = $locale;
-
- if (isset(self::$_cache)) {
- $id = 'Zend_Translate_' . $this->toString() . '_Options';
- if (self::$_cacheTags) {
- self::$_cache->save($this->_options, $id, array($this->_options['tag']));
- } else {
- self::$_cache->save($this->_options, $id);
- }
- }
- }
-
- return $this;
- }
-
- /**
- * Returns the available languages from this adapter
- *
- * @return array|null
- */
- public function getList()
- {
- $list = array_keys($this->_translate);
- $result = null;
- foreach($list as $value) {
- if (!empty($this->_translate[$value])) {
- $result[$value] = $value;
- }
- }
- return $result;
- }
-
- /**
- * Returns the message id for a given translation
- * If no locale is given, the actual language will be used
- *
- * @param string $message Message to get the key for
- * @param string|Zend_Locale $locale (optional) Language to return the message ids from
- * @return string|array|false
- */
- public function getMessageId($message, $locale = null)
- {
- if (empty($locale) or !$this->isAvailable($locale)) {
- $locale = $this->_options['locale'];
- }
-
- return array_search($message, $this->_translate[(string) $locale]);
- }
-
- /**
- * Returns all available message ids from this adapter
- * If no locale is given, the actual language will be used
- *
- * @param string|Zend_Locale $locale (optional) Language to return the message ids from
- * @return array
- */
- public function getMessageIds($locale = null)
- {
- if (empty($locale) or !$this->isAvailable($locale)) {
- $locale = $this->_options['locale'];
- }
-
- return array_keys($this->_translate[(string) $locale]);
- }
-
- /**
- * Returns all available translations from this adapter
- * If no locale is given, the actual language will be used
- * If 'all' is given the complete translation dictionary will be returned
- *
- * @param string|Zend_Locale $locale (optional) Language to return the messages from
- * @return array
- */
- public function getMessages($locale = null)
- {
- if ($locale === 'all') {
- return $this->_translate;
- }
-
- if ((empty($locale) === true) or ($this->isAvailable($locale) === false)) {
- $locale = $this->_options['locale'];
- }
-
- return $this->_translate[(string) $locale];
- }
-
- /**
- * Is the wished language available ?
- *
- * @see Zend_Locale
- * @param string|Zend_Locale $locale Language to search for, identical with locale identifier,
- * @see Zend_Locale for more information
- * @return boolean
- */
- public function isAvailable($locale)
- {
- $return = isset($this->_translate[(string) $locale]);
- return $return;
- }
-
- /**
- * Load translation data
- *
- * @param mixed $data
- * @param string|Zend_Locale $locale
- * @param array $options (optional)
- * @return array
- */
- abstract protected function _loadTranslationData($data, $locale, array $options = array());
-
- /**
- * Internal function for adding translation data
- *
- * This may be a new language or additional data for an existing language
- * If the options 'clear' is true, then the translation data for the specified
- * language is replaced and added otherwise
- *
- * @see Zend_Locale
- * @param array|Zend_Config $content Translation data to add
- * @throws Zend_Translate_Exception
- * @return Zend_Translate_Adapter Provides fluent interface
- */
- private function _addTranslationData($options = array())
- {
- if ($options instanceof Zend_Config) {
- $options = $options->toArray();
- } else if (func_num_args() > 1) {
- $args = func_get_args();
- $options['content'] = array_shift($args);
-
- if (!empty($args)) {
- $options['locale'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $options += array_shift($args);
- }
- }
-
- if (($options['content'] instanceof Zend_Translate) || ($options['content'] instanceof Zend_Translate_Adapter)) {
- $options['usetranslateadapter'] = true;
- if (!empty($options['locale']) && ($options['locale'] !== 'auto')) {
- $options['content'] = $options['content']->getMessages($options['locale']);
- } else {
- $content = $options['content'];
- $locales = $content->getList();
- foreach ($locales as $locale) {
- $options['locale'] = $locale;
- $options['content'] = $content->getMessages($locale);
- $this->_addTranslationData($options);
- }
-
- return $this;
- }
- }
-
- try {
- $options['locale'] = Zend_Locale::findLocale($options['locale']);
- } catch (Zend_Locale_Exception $e) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
- }
-
- if ($options['clear'] || !isset($this->_translate[$options['locale']])) {
- $this->_translate[$options['locale']] = array();
- }
-
- $read = true;
- if (isset(self::$_cache)) {
- $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
- $temp = self::$_cache->load($id);
- if ($temp) {
- $read = false;
- }
- }
-
- if ($options['reload']) {
- $read = true;
- }
-
- if ($read) {
- if (!empty($options['usetranslateadapter'])) {
- $temp = array($options['locale'] => $options['content']);
- } else {
- $temp = $this->_loadTranslationData($options['content'], $options['locale'], $options);
- }
- }
-
- if (empty($temp)) {
- $temp = array();
- }
-
- $keys = array_keys($temp);
- foreach($keys as $key) {
- if (!isset($this->_translate[$key])) {
- $this->_translate[$key] = array();
- }
-
- if (array_key_exists($key, $temp) && is_array($temp[$key])) {
- $this->_translate[$key] = $temp[$key] + $this->_translate[$key];
- }
- }
-
- if ($this->_automatic === true) {
- $find = new Zend_Locale($options['locale']);
- $browser = $find->getEnvironment() + $find->getBrowser();
- arsort($browser);
- foreach($browser as $language => $quality) {
- if (isset($this->_translate[$language])) {
- $this->_options['locale'] = $language;
- break;
- }
- }
- }
-
- if (($read) and (isset(self::$_cache))) {
- $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
- if (self::$_cacheTags) {
- self::$_cache->save($temp, $id, array($this->_options['tag']));
- } else {
- self::$_cache->save($temp, $id);
- }
- }
-
- return $this;
- }
-
- /**
- * Translates the given string
- * returns the translation
- *
- * @see Zend_Locale
- * @param string|array $messageId Translation string, or Array for plural translations
- * @param string|Zend_Locale $locale (optional) Locale/Language to use, identical with
- * locale identifier, @see Zend_Locale for more information
- * @return string
- */
- public function translate($messageId, $locale = null)
- {
- if ($locale === null) {
- $locale = $this->_options['locale'];
- }
-
- $plural = null;
- if (is_array($messageId)) {
- if (count($messageId) > 2) {
- $number = array_pop($messageId);
- if (!is_numeric($number)) {
- $plocale = $number;
- $number = array_pop($messageId);
- } else {
- $plocale = 'en';
- }
-
- $plural = $messageId;
- $messageId = $messageId[0];
- } else {
- $messageId = $messageId[0];
- }
- }
-
- // CUSTOM ischommer: Skip locale checks, too computationally expensive.
- // Assume correct locale value is passed in.
- // if (!Zend_Locale::isLocale($locale, true, false)) {
- // if (!Zend_Locale::isLocale($locale, false, false)) {
- // // language does not exist, return original string
- // $this->_log($messageId, $locale);
- // // use rerouting when enabled
- // if (!empty($this->_options['route'])) {
- // if (array_key_exists($locale, $this->_options['route']) &&
- // !array_key_exists($locale, $this->_routed)) {
- // $this->_routed[$locale] = true;
- // return $this->translate($messageId, $this->_options['route'][$locale]);
- // }
- // }
-
- // $this->_routed = array();
- // if ($plural === null) {
- // return $messageId;
- // }
-
- // $rule = Zend_Translate_Plural::getPlural($number, $plocale);
- // if (!isset($plural[$rule])) {
- // $rule = 0;
- // }
-
- // return $plural[$rule];
- // }
-
- // $locale = new Zend_Locale($locale);
- // }
- // CUSTOM END
-
- $locale = (string) $locale;
- if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
- // return original translation
- if ($plural === null) {
- $this->_routed = array();
- return $this->_translate[$locale][$messageId];
- }
-
- $rule = Zend_Translate_Plural::getPlural($number, $locale);
- if (isset($this->_translate[$locale][$plural[0]][$rule])) {
- $this->_routed = array();
- return $this->_translate[$locale][$plural[0]][$rule];
- }
- } else if (strlen($locale) != 2) {
- // faster than creating a new locale and separate the leading part
- $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
-
- if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
- // return regionless translation (en_US -> en)
- if ($plural === null) {
- $this->_routed = array();
- return $this->_translate[$locale][$messageId];
- }
-
- $rule = Zend_Translate_Plural::getPlural($number, $locale);
- if (isset($this->_translate[$locale][$plural[0]][$rule])) {
- $this->_routed = array();
- return $this->_translate[$locale][$plural[0]][$rule];
- }
- }
- }
-
- $this->_log($messageId, $locale);
- // use rerouting when enabled
- if (!empty($this->_options['route'])) {
- if (array_key_exists($locale, $this->_options['route']) &&
- !array_key_exists($locale, $this->_routed)) {
- $this->_routed[$locale] = true;
- return $this->translate($messageId, $this->_options['route'][$locale]);
- }
- }
-
- $this->_routed = array();
- if ($plural === null) {
- return $messageId;
- }
-
- $rule = Zend_Translate_Plural::getPlural($number, $plocale);
- if (!isset($plural[$rule])) {
- $rule = 0;
- }
-
- return $plural[$rule];
- }
-
- /**
- * Translates the given string using plural notations
- * Returns the translated string
- *
- * @see Zend_Locale
- * @param string $singular Singular translation string
- * @param string $plural Plural translation string
- * @param integer $number Number for detecting the correct plural
- * @param string|Zend_Locale $locale (Optional) Locale/Language to use, identical with
- * locale identifier, @see Zend_Locale for more information
- * @return string
- */
- public function plural($singular, $plural, $number, $locale = null)
- {
- return $this->translate(array($singular, $plural, $number), $locale);
- }
-
- /**
- * Logs a message when the log option is set
- *
- * @param string $message Message to log
- * @param String $locale Locale to log
- */
- protected function _log($message, $locale) {
- if ($this->_options['logUntranslated']) {
- $message = str_replace('%message%', $message, $this->_options['logMessage']);
- $message = str_replace('%locale%', $locale, $message);
- if ($this->_options['log']) {
- $this->_options['log']->log($message, $this->_options['logPriority']);
- } else {
- trigger_error($message, E_USER_NOTICE);
- }
- }
- }
-
- /**
- * Translates the given string
- * returns the translation
- *
- * @param string $messageId Translation string
- * @param string|Zend_Locale $locale (optional) Locale/Language to use, identical with locale
- * identifier, @see Zend_Locale for more information
- * @return string
- */
- public function _($messageId, $locale = null)
- {
- return $this->translate($messageId, $locale);
- }
-
- /**
- * Checks if a string is translated within the source or not
- * returns boolean
- *
- * @param string $messageId Translation string
- * @param boolean $original (optional) Allow translation only for original language
- * when true, a translation for 'en_US' would give false when it can
- * be translated with 'en' only
- * @param string|Zend_Locale $locale (optional) Locale/Language to use, identical with locale identifier,
- * see Zend_Locale for more information
- * @return boolean
- */
- public function isTranslated($messageId, $original = false, $locale = null)
- {
- if (($original !== false) and ($original !== true)) {
- $locale = $original;
- $original = false;
- }
-
- if ($locale === null) {
- $locale = $this->_options['locale'];
- }
-
- if (!Zend_Locale::isLocale($locale, true, false)) {
- if (!Zend_Locale::isLocale($locale, false, false)) {
- // language does not exist, return original string
- return false;
- }
-
- $locale = new Zend_Locale($locale);
- }
-
- $locale = (string) $locale;
- if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
- // return original translation
- return true;
- } else if ((strlen($locale) != 2) and ($original === false)) {
- // faster than creating a new locale and separate the leading part
- $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
-
- if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
- // return regionless translation (en_US -> en)
- return true;
- }
- }
-
- // No translation found, return original
- return false;
- }
-
- /**
- * Returns the set cache
- *
- * @return Zend_Cache_Core The set cache
- */
- public static function getCache()
- {
- return self::$_cache;
- }
-
- /**
- * Sets a cache for all Zend_Translate_Adapters
- *
- * @param Zend_Cache_Core $cache Cache to store to
- */
- public static function setCache(Zend_Cache_Core $cache)
- {
- self::$_cache = $cache;
- self::_getTagSupportForCache();
- }
-
- /**
- * Returns true when a cache is set
- *
- * @return boolean
- */
- public static function hasCache()
- {
- if (self::$_cache !== null) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Removes any set cache
- *
- * @return void
- */
- public static function removeCache()
- {
- self::$_cache = null;
- }
-
- /**
- * Clears all set cache data
- *
- * @param string $tag Tag to clear when the default tag name is not used
- * @return void
- */
- public static function clearCache($tag = null)
- {
- require_once 'Zend/Cache.php';
- if (self::$_cacheTags) {
- if ($tag == null) {
- $tag = 'Zend_Translate';
- }
-
- self::$_cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($tag));
- } else {
- self::$_cache->clean(Zend_Cache::CLEANING_MODE_ALL);
- }
- }
-
- /**
- * Returns the adapter name
- *
- * @return string
- */
- abstract public function toString();
-
- /**
- * Internal method to check if the given cache supports tags
- *
- * @param Zend_Cache $cache
- */
- private static function _getTagSupportForCache()
- {
- $backend = self::$_cache->getBackend();
- if ($backend instanceof Zend_Cache_Backend_ExtendedInterface) {
- $cacheOptions = $backend->getCapabilities();
- self::$_cacheTags = $cacheOptions['tags'];
- } else {
- self::$_cacheTags = false;
- }
-
- return self::$_cacheTags;
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Array.php b/thirdparty/Zend/Translate/Adapter/Array.php
deleted file mode 100644
index cbecbb934f2..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Array.php
+++ /dev/null
@@ -1,81 +0,0 @@
-_data = array();
- if (!is_array($data)) {
- if (file_exists($data)) {
- ob_start();
- $data = include($data);
- ob_end_clean();
- }
- }
- if (!is_array($data)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("Error including array or file '".$data."'");
- }
-
- if (!isset($this->_data[$locale])) {
- $this->_data[$locale] = array();
- }
-
- $this->_data[$locale] = $data + $this->_data[$locale];
- return $this->_data;
- }
-
- /**
- * returns the adapters name
- *
- * @return string
- */
- public function toString()
- {
- return "Array";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Csv.php b/thirdparty/Zend/Translate/Adapter/Csv.php
deleted file mode 100644
index f24437dd961..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Csv.php
+++ /dev/null
@@ -1,121 +0,0 @@
-_options['delimiter'] = ";";
- $this->_options['length'] = 0;
- $this->_options['enclosure'] = '"';
-
- if ($options instanceof Zend_Config) {
- $options = $options->toArray();
- } else if (func_num_args() > 1) {
- $args = func_get_args();
- $options = array();
- $options['content'] = array_shift($args);
-
- if (!empty($args)) {
- $options['locale'] = array_shift($args);
- }
-
- if (!empty($args)) {
- $opt = array_shift($args);
- $options = array_merge($opt, $options);
- }
- } else if (!is_array($options)) {
- $options = array('content' => $options);
- }
-
- parent::__construct($options);
- }
-
- /**
- * Load translation data
- *
- * @param string|array $filename Filename and full path to the translation source
- * @param string $locale Locale/Language to add data for, identical with locale identifier,
- * see Zend_Locale for more information
- * @param array $option OPTIONAL Options to use
- * @return array
- */
- protected function _loadTranslationData($filename, $locale, array $options = array())
- {
- $this->_data = array();
- $options = $options + $this->_options;
- $this->_file = @fopen($filename, 'rb');
- if (!$this->_file) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Error opening translation file \'' . $filename . '\'.');
- }
-
- while(($data = fgetcsv($this->_file, $options['length'], $options['delimiter'], $options['enclosure'])) !== false) {
- if (substr($data[0], 0, 1) === '#') {
- continue;
- }
-
- if (!isset($data[1])) {
- continue;
- }
-
- if (count($data) == 2) {
- $this->_data[$locale][$data[0]] = $data[1];
- } else {
- $singular = array_shift($data);
- $this->_data[$locale][$singular] = $data;
- }
- }
-
- return $this->_data;
- }
-
- /**
- * returns the adapters name
- *
- * @return string
- */
- public function toString()
- {
- return "Csv";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Gettext.php b/thirdparty/Zend/Translate/Adapter/Gettext.php
deleted file mode 100644
index de06c107a0c..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Gettext.php
+++ /dev/null
@@ -1,169 +0,0 @@
-_bigEndian === false) {
- return unpack('V' . $bytes, fread($this->_file, 4 * $bytes));
- } else {
- return unpack('N' . $bytes, fread($this->_file, 4 * $bytes));
- }
- }
-
- /**
- * Load translation data (MO file reader)
- *
- * @param string $filename MO file to add, full path must be given for access
- * @param string $locale New Locale/Language to set, identical with locale identifier,
- * see Zend_Locale for more information
- * @param array $option OPTIONAL Options to use
- * @throws Zend_Translation_Exception
- * @return array
- */
- protected function _loadTranslationData($filename, $locale, array $options = array())
- {
- $this->_data = array();
- $this->_bigEndian = false;
- $this->_file = @fopen($filename, 'rb');
- if (!$this->_file) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Error opening translation file \'' . $filename . '\'.');
- }
- if (@filesize($filename) < 10) {
- @fclose($this->_file);
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('\'' . $filename . '\' is not a gettext file');
- }
-
- // get Endian
- $input = $this->_readMOData(1);
- if (strtolower(substr(dechex($input[1]), -8)) == "950412de") {
- $this->_bigEndian = false;
- } else if (strtolower(substr(dechex($input[1]), -8)) == "de120495") {
- $this->_bigEndian = true;
- } else {
- @fclose($this->_file);
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('\'' . $filename . '\' is not a gettext file');
- }
- // read revision - not supported for now
- $input = $this->_readMOData(1);
-
- // number of bytes
- $input = $this->_readMOData(1);
- $total = $input[1];
-
- // number of original strings
- $input = $this->_readMOData(1);
- $OOffset = $input[1];
-
- // number of translation strings
- $input = $this->_readMOData(1);
- $TOffset = $input[1];
-
- // fill the original table
- fseek($this->_file, $OOffset);
- $origtemp = $this->_readMOData(2 * $total);
- fseek($this->_file, $TOffset);
- $transtemp = $this->_readMOData(2 * $total);
-
- for($count = 0; $count < $total; ++$count) {
- if ($origtemp[$count * 2 + 1] != 0) {
- fseek($this->_file, $origtemp[$count * 2 + 2]);
- $original = @fread($this->_file, $origtemp[$count * 2 + 1]);
- $original = explode("\0", $original);
- } else {
- $original[0] = '';
- }
-
- if ($transtemp[$count * 2 + 1] != 0) {
- fseek($this->_file, $transtemp[$count * 2 + 2]);
- $translate = fread($this->_file, $transtemp[$count * 2 + 1]);
- $translate = explode("\0", $translate);
- if ((count($original) > 1) && (count($translate) > 1)) {
- $this->_data[$locale][$original[0]] = $translate;
- array_shift($original);
- foreach ($original as $orig) {
- $this->_data[$locale][$orig] = '';
- }
- } else {
- $this->_data[$locale][$original[0]] = $translate[0];
- }
- }
- }
-
- @fclose($this->_file);
-
- $this->_data[$locale][''] = trim($this->_data[$locale]['']);
- if (empty($this->_data[$locale][''])) {
- $this->_adapterInfo[$filename] = 'No adapter information available';
- } else {
- $this->_adapterInfo[$filename] = $this->_data[$locale][''];
- }
-
- unset($this->_data[$locale]['']);
- return $this->_data;
- }
-
- /**
- * Returns the adapter informations
- *
- * @return array Each loaded adapter information as array value
- */
- public function getAdapterInfo()
- {
- return $this->_adapterInfo;
- }
-
- /**
- * Returns the adapter name
- *
- * @return string
- */
- public function toString()
- {
- return "Gettext";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Ini.php b/thirdparty/Zend/Translate/Adapter/Ini.php
deleted file mode 100644
index 4dfdb649215..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Ini.php
+++ /dev/null
@@ -1,74 +0,0 @@
-_data = array();
- if (!file_exists($data)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception("Ini file '".$data."' not found");
- }
-
- $inidata = parse_ini_file($data, false);
- if (!isset($this->_data[$locale])) {
- $this->_data[$locale] = array();
- }
-
- $this->_data[$locale] = array_merge($this->_data[$locale], $inidata);
- return $this->_data;
- }
-
- /**
- * returns the adapters name
- *
- * @return string
- */
- public function toString()
- {
- return "Ini";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Qt.php b/thirdparty/Zend/Translate/Adapter/Qt.php
deleted file mode 100644
index 27ff7121738..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Qt.php
+++ /dev/null
@@ -1,160 +0,0 @@
-_data = array();
- if (!is_readable($filename)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Translation file \'' . $filename . '\' is not readable.');
- }
-
- $this->_target = $locale;
-
- $encoding = $this->_findEncoding($filename);
- $this->_file = xml_parser_create($encoding);
- xml_set_object($this->_file, $this);
- xml_parser_set_option($this->_file, XML_OPTION_CASE_FOLDING, 0);
- xml_set_element_handler($this->_file, "_startElement", "_endElement");
- xml_set_character_data_handler($this->_file, "_contentElement");
-
- if (!xml_parse($this->_file, file_get_contents($filename))) {
- $ex = sprintf('XML error: %s at line %d',
- xml_error_string(xml_get_error_code($this->_file)),
- xml_get_current_line_number($this->_file));
- xml_parser_free($this->_file);
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception($ex);
- }
-
- return $this->_data;
- }
-
- private function _startElement($file, $name, $attrib)
- {
- switch(strtolower($name)) {
- case 'message':
- $this->_source = null;
- $this->_stag = false;
- $this->_ttag = false;
- $this->_scontent = null;
- $this->_tcontent = null;
- break;
- case 'source':
- $this->_stag = true;
- break;
- case 'translation':
- $this->_ttag = true;
- break;
- default:
- break;
- }
- }
-
- private function _endElement($file, $name)
- {
- switch (strtolower($name)) {
- case 'source':
- $this->_stag = false;
- break;
-
- case 'translation':
- if (!empty($this->_scontent) and !empty($this->_tcontent) or
- (isset($this->_data[$this->_target][$this->_scontent]) === false)) {
- $this->_data[$this->_target][$this->_scontent] = $this->_tcontent;
- }
- $this->_ttag = false;
- break;
-
- default:
- break;
- }
- }
-
- private function _contentElement($file, $data)
- {
- if ($this->_stag === true) {
- $this->_scontent .= $data;
- }
-
- if ($this->_ttag === true) {
- $this->_tcontent .= $data;
- }
- }
-
- private function _findEncoding($filename)
- {
- $file = file_get_contents($filename, null, null, 0, 100);
- if (strpos($file, "encoding") !== false) {
- $encoding = substr($file, strpos($file, "encoding") + 9);
- $encoding = substr($encoding, 1, strpos($encoding, $encoding[0], 1) - 1);
- return $encoding;
- }
- return 'UTF-8';
- }
-
- /**
- * Returns the adapter name
- *
- * @return string
- */
- public function toString()
- {
- return "Qt";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Tbx.php b/thirdparty/Zend/Translate/Adapter/Tbx.php
deleted file mode 100644
index 1436c5eb036..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Tbx.php
+++ /dev/null
@@ -1,165 +0,0 @@
-_data = array();
- if (!is_readable($filename)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Translation file \'' . $filename . '\' is not readable.');
- }
-
- $encoding = $this->_findEncoding($filename);
- $this->_file = xml_parser_create($encoding);
- xml_set_object($this->_file, $this);
- xml_parser_set_option($this->_file, XML_OPTION_CASE_FOLDING, 0);
- xml_set_element_handler($this->_file, "_startElement", "_endElement");
- xml_set_character_data_handler($this->_file, "_contentElement");
-
- if (!xml_parse($this->_file, file_get_contents($filename))) {
- $ex = sprintf('XML error: %s at line %d',
- xml_error_string(xml_get_error_code($this->_file)),
- xml_get_current_line_number($this->_file));
- xml_parser_free($this->_file);
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception($ex);
- }
-
- return $this->_data;
- }
-
- private function _startElement($file, $name, $attrib)
- {
- if ($this->_term !== null) {
- $this->_content .= "<".$name;
- foreach($attrib as $key => $value) {
- $this->_content .= " $key=\"$value\"";
- }
- $this->_content .= ">";
- } else {
- switch(strtolower($name)) {
- case 'termentry':
- $this->_termentry = null;
- break;
- case 'langset':
- if (isset($attrib['xml:lang']) === true) {
- $this->_langset = $attrib['xml:lang'];
- if (isset($this->_data[$this->_langset]) === false) {
- $this->_data[$this->_langset] = array();
- }
- }
- break;
- case 'term':
- $this->_term = true;
- $this->_content = null;
- break;
- default:
- break;
- }
- }
- }
-
- private function _endElement($file, $name)
- {
- if (($this->_term !== null) and ($name != "term")) {
- $this->_content .= "".$name.">";
- } else {
- switch (strtolower($name)) {
- case 'langset':
- $this->_langset = null;
- break;
- case 'term':
- $this->_term = null;
- if (empty($this->_termentry)) {
- $this->_termentry = $this->_content;
- }
- if (!empty($this->_content) or (isset($this->_data[$this->_langset][$this->_termentry]) === false)) {
- $this->_data[$this->_langset][$this->_termentry] = $this->_content;
- }
- break;
- default:
- break;
- }
- }
- }
-
- private function _contentElement($file, $data)
- {
- if ($this->_term !== null) {
- $this->_content .= $data;
- }
- }
-
- private function _findEncoding($filename)
- {
- $file = file_get_contents($filename, null, null, 0, 100);
- if (strpos($file, "encoding") !== false) {
- $encoding = substr($file, strpos($file, "encoding") + 9);
- $encoding = substr($encoding, 1, strpos($encoding, $encoding[0], 1) - 1);
- return $encoding;
- }
- return 'UTF-8';
- }
-
- /**
- * Returns the adapter name
- *
- * @return string
- */
- public function toString()
- {
- return "Tbx";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Tmx.php b/thirdparty/Zend/Translate/Adapter/Tmx.php
deleted file mode 100644
index 31c8f1bf430..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Tmx.php
+++ /dev/null
@@ -1,233 +0,0 @@
-_data = array();
- if (!is_readable($filename)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Translation file \'' . $filename . '\' is not readable.');
- }
-
- if (isset($options['useId'])) {
- $this->_useId = (boolean) $options['useId'];
- }
-
- $encoding = $this->_findEncoding($filename);
- $this->_file = xml_parser_create($encoding);
- xml_set_object($this->_file, $this);
- xml_parser_set_option($this->_file, XML_OPTION_CASE_FOLDING, 0);
- xml_set_element_handler($this->_file, "_startElement", "_endElement");
- xml_set_character_data_handler($this->_file, "_contentElement");
-
- if (!xml_parse($this->_file, file_get_contents($filename))) {
- $ex = sprintf('XML error: %s at line %d',
- xml_error_string(xml_get_error_code($this->_file)),
- xml_get_current_line_number($this->_file));
- xml_parser_free($this->_file);
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception($ex);
- }
-
- return $this->_data;
- }
-
- /**
- * Internal method, called by xml element handler at start
- *
- * @param resource $file File handler
- * @param string $name Elements name
- * @param array $attrib Attributes for this element
- */
- protected function _startElement($file, $name, $attrib)
- {
- if ($this->_seg !== null) {
- $this->_content .= "<".$name;
- foreach($attrib as $key => $value) {
- $this->_content .= " $key=\"$value\"";
- }
- $this->_content .= ">";
- } else {
- switch(strtolower($name)) {
- case 'header':
- if (empty($this->_useId) && isset($attrib['srclang'])) {
- if (Zend_Locale::isLocale($attrib['srclang'])) {
- $this->_srclang = Zend_Locale::findLocale($attrib['srclang']);
- } else {
- if (!$this->_options['disableNotices']) {
- if ($this->_options['log']) {
- $this->_options['log']->notice("The language '{$attrib['srclang']}' can not be set because it does not exist.");
- } else {
- trigger_error("The language '{$attrib['srclang']}' can not be set because it does not exist.", E_USER_NOTICE);
- }
- }
-
- $this->_srclang = $attrib['srclang'];
- }
- }
- break;
- case 'tu':
- if (isset($attrib['tuid'])) {
- $this->_tu = $attrib['tuid'];
- }
- break;
- case 'tuv':
- if (isset($attrib['xml:lang'])) {
- if (Zend_Locale::isLocale($attrib['xml:lang'])) {
- $this->_tuv = Zend_Locale::findLocale($attrib['xml:lang']);
- } else {
- if (!$this->_options['disableNotices']) {
- if ($this->_options['log']) {
- $this->_options['log']->notice("The language '{$attrib['xml:lang']}' can not be set because it does not exist.");
- } else {
- trigger_error("The language '{$attrib['xml:lang']}' can not be set because it does not exist.", E_USER_NOTICE);
- }
- }
-
- $this->_tuv = $attrib['xml:lang'];
- }
-
- if (!isset($this->_data[$this->_tuv])) {
- $this->_data[$this->_tuv] = array();
- }
- }
- break;
- case 'seg':
- $this->_seg = true;
- $this->_content = null;
- break;
- default:
- break;
- }
- }
- }
-
-
- /**
- * Internal method, called by xml element handler at end
- *
- * @param resource $file File handler
- * @param string $name Elements name
- */
- protected function _endElement($file, $name)
- {
- if (($this->_seg !== null) and ($name !== 'seg')) {
- $this->_content .= "".$name.">";
- } else {
- switch (strtolower($name)) {
- case 'tu':
- $this->_tu = null;
- break;
- case 'tuv':
- $this->_tuv = null;
- break;
- case 'seg':
- $this->_seg = null;
- if (!empty($this->_srclang) && ($this->_srclang == $this->_tuv)) {
- $this->_tu = $this->_content;
- }
-
- if (!empty($this->_content) or (!isset($this->_data[$this->_tuv][$this->_tu]))) {
- $this->_data[$this->_tuv][$this->_tu] = $this->_content;
- }
- break;
- default:
- break;
- }
- }
- }
-
- /**
- * Internal method, called by xml element handler for content
- *
- * @param resource $file File handler
- * @param string $data Elements content
- */
- protected function _contentElement($file, $data)
- {
- if (($this->_seg !== null) and ($this->_tu !== null) and ($this->_tuv !== null)) {
- $this->_content .= $data;
- }
- }
-
-
- /**
- * Internal method, detects the encoding of the xml file
- *
- * @param string $name Filename
- * @return string Encoding
- */
- protected function _findEncoding($filename)
- {
- $file = file_get_contents($filename, null, null, 0, 100);
- if (strpos($file, "encoding") !== false) {
- $encoding = substr($file, strpos($file, "encoding") + 9);
- $encoding = substr($encoding, 1, strpos($encoding, $encoding[0], 1) - 1);
- return $encoding;
- }
- return 'UTF-8';
- }
-
- /**
- * Returns the adapter name
- *
- * @return string
- */
- public function toString()
- {
- return "Tmx";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/Xliff.php b/thirdparty/Zend/Translate/Adapter/Xliff.php
deleted file mode 100644
index 2fc12ff9618..00000000000
--- a/thirdparty/Zend/Translate/Adapter/Xliff.php
+++ /dev/null
@@ -1,229 +0,0 @@
-_data = array();
- if (!is_readable($filename)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Translation file \'' . $filename . '\' is not readable.');
- }
-
- if (empty($options['useId'])) {
- $this->_useId = false;
- } else {
- $this->_useId = true;
- }
-
- $encoding = $this->_findEncoding($filename);
- $this->_target = $locale;
- $this->_file = xml_parser_create($encoding);
- xml_set_object($this->_file, $this);
- xml_parser_set_option($this->_file, XML_OPTION_CASE_FOLDING, 0);
- xml_set_element_handler($this->_file, "_startElement", "_endElement");
- xml_set_character_data_handler($this->_file, "_contentElement");
-
- if (!xml_parse($this->_file, file_get_contents($filename))) {
- $ex = sprintf('XML error: %s at line %d',
- xml_error_string(xml_get_error_code($this->_file)),
- xml_get_current_line_number($this->_file));
- xml_parser_free($this->_file);
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception($ex);
- }
-
- return $this->_data;
- }
-
- private function _startElement($file, $name, $attrib)
- {
- if ($this->_stag === true) {
- $this->_scontent .= "<".$name;
- foreach($attrib as $key => $value) {
- $this->_scontent .= " $key=\"$value\"";
- }
- $this->_scontent .= ">";
- } else if ($this->_ttag === true) {
- $this->_tcontent .= "<".$name;
- foreach($attrib as $key => $value) {
- $this->_tcontent .= " $key=\"$value\"";
- }
- $this->_tcontent .= ">";
- } else {
- switch(strtolower($name)) {
- case 'file':
- $this->_source = $attrib['source-language'];
- if (isset($attrib['target-language'])) {
- $this->_target = $attrib['target-language'];
- }
-
- if (!isset($this->_data[$this->_source])) {
- $this->_data[$this->_source] = array();
- }
-
- if (!isset($this->_data[$this->_target])) {
- $this->_data[$this->_target] = array();
- }
-
- break;
- case 'trans-unit':
- $this->_transunit = true;
- $this->_langId = $attrib['id'];
- break;
- case 'source':
- if ($this->_transunit === true) {
- $this->_scontent = null;
- $this->_stag = true;
- $this->_ttag = false;
- }
- break;
- case 'target':
- if ($this->_transunit === true) {
- $this->_tcontent = null;
- $this->_ttag = true;
- $this->_stag = false;
- }
- break;
- default:
- break;
- }
- }
- }
-
- private function _endElement($file, $name)
- {
- if (($this->_stag === true) and ($name !== 'source')) {
- $this->_scontent .= "".$name.">";
- } else if (($this->_ttag === true) and ($name !== 'target')) {
- $this->_tcontent .= "".$name.">";
- } else {
- switch (strtolower($name)) {
- case 'trans-unit':
- $this->_transunit = null;
- $this->_langId = null;
- $this->_scontent = null;
- $this->_tcontent = null;
- break;
- case 'source':
- if ($this->_useId) {
- if (!empty($this->_scontent) && !empty($this->_langId) &&
- !isset($this->_data[$this->_source][$this->_langId])) {
- $this->_data[$this->_source][$this->_langId] = $this->_scontent;
- }
- } else {
- if (!empty($this->_scontent) &&
- !isset($this->_data[$this->_source][$this->_scontent])) {
- $this->_data[$this->_source][$this->_scontent] = $this->_scontent;
- }
- }
- $this->_stag = false;
- break;
- case 'target':
- if ($this->_useId) {
- if (!empty($this->_tcontent) && !empty($this->_langId) &&
- !isset($this->_data[$this->_target][$this->_langId])) {
- $this->_data[$this->_target][$this->_langId] = $this->_tcontent;
- }
- } else {
- if (!empty($this->_tcontent) && !empty($this->_scontent) &&
- !isset($this->_data[$this->_target][$this->_scontent])) {
- $this->_data[$this->_target][$this->_scontent] = $this->_tcontent;
- }
- }
- $this->_ttag = false;
- break;
- default:
- break;
- }
- }
- }
-
- private function _contentElement($file, $data)
- {
- if (($this->_transunit !== null) and ($this->_source !== null) and ($this->_stag === true)) {
- $this->_scontent .= $data;
- }
-
- if (($this->_transunit !== null) and ($this->_target !== null) and ($this->_ttag === true)) {
- $this->_tcontent .= $data;
- }
- }
-
- private function _findEncoding($filename)
- {
- $file = file_get_contents($filename, null, null, 0, 100);
- if (strpos($file, "encoding") !== false) {
- $encoding = substr($file, strpos($file, "encoding") + 9);
- $encoding = substr($encoding, 1, strpos($encoding, $encoding[0], 1) - 1);
- return $encoding;
- }
- return 'UTF-8';
- }
-
- /**
- * Returns the adapter name
- *
- * @return string
- */
- public function toString()
- {
- return "Xliff";
- }
-}
diff --git a/thirdparty/Zend/Translate/Adapter/XmlTm.php b/thirdparty/Zend/Translate/Adapter/XmlTm.php
deleted file mode 100644
index a0e5f8839bf..00000000000
--- a/thirdparty/Zend/Translate/Adapter/XmlTm.php
+++ /dev/null
@@ -1,139 +0,0 @@
-_data = array();
- $this->_lang = $locale;
- if (!is_readable($filename)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('Translation file \'' . $filename . '\' is not readable.');
- }
-
- $encoding = $this->_findEncoding($filename);
- $this->_file = xml_parser_create($encoding);
- xml_set_object($this->_file, $this);
- xml_parser_set_option($this->_file, XML_OPTION_CASE_FOLDING, 0);
- xml_set_element_handler($this->_file, "_startElement", "_endElement");
- xml_set_character_data_handler($this->_file, "_contentElement");
-
- if (!xml_parse($this->_file, file_get_contents($filename))) {
- $ex = sprintf('XML error: %s at line %d',
- xml_error_string(xml_get_error_code($this->_file)),
- xml_get_current_line_number($this->_file));
- xml_parser_free($this->_file);
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception($ex);
- }
-
- return $this->_data;
- }
-
- private function _startElement($file, $name, $attrib)
- {
- switch(strtolower($name)) {
- case 'tm:tu':
- $this->_tag = $attrib['id'];
- $this->_content = null;
- break;
- default:
- break;
- }
- }
-
- private function _endElement($file, $name)
- {
- switch (strtolower($name)) {
- case 'tm:tu':
- if (!empty($this->_tag) and !empty($this->_content) or
- (isset($this->_data[$this->_lang][$this->_tag]) === false)) {
- $this->_data[$this->_lang][$this->_tag] = $this->_content;
- }
- $this->_tag = null;
- $this->_content = null;
- break;
-
- default:
- break;
- }
- }
-
- private function _contentElement($file, $data)
- {
- if (($this->_tag !== null)) {
- $this->_content .= $data;
- }
- }
-
- private function _findEncoding($filename)
- {
- $file = file_get_contents($filename, null, null, 0, 100);
- if (strpos($file, "encoding") !== false) {
- $encoding = substr($file, strpos($file, "encoding") + 9);
- $encoding = substr($encoding, 1, strpos($encoding, $encoding[0], 1) - 1);
- return $encoding;
- }
- return 'UTF-8';
- }
-
- /**
- * Returns the adapter name
- *
- * @return string
- */
- public function toString()
- {
- return "XmlTm";
- }
-}
diff --git a/thirdparty/Zend/Translate/Exception.php b/thirdparty/Zend/Translate/Exception.php
deleted file mode 100644
index 66f910900bc..00000000000
--- a/thirdparty/Zend/Translate/Exception.php
+++ /dev/null
@@ -1,37 +0,0 @@
- 3) {
- $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
- }
-
- if (isset(self::$_plural[$locale])) {
- $return = call_user_func(self::$_plural[$locale], $number);
-
- if (!is_int($return) || ($return < 0)) {
- $return = 0;
- }
-
- return $return;
- }
-
- switch($locale) {
- case 'bo':
- case 'dz':
- case 'id':
- case 'ja':
- case 'jv':
- case 'ka':
- case 'km':
- case 'kn':
- case 'ko':
- case 'ms':
- case 'th':
- case 'tr':
- case 'vi':
- case 'zh':
- return 0;
- break;
-
- case 'af':
- case 'az':
- case 'bn':
- case 'bg':
- case 'ca':
- case 'da':
- case 'de':
- case 'el':
- case 'en':
- case 'eo':
- case 'es':
- case 'et':
- case 'eu':
- case 'fa':
- case 'fi':
- case 'fo':
- case 'fur':
- case 'fy':
- case 'gl':
- case 'gu':
- case 'ha':
- case 'he':
- case 'hu':
- case 'is':
- case 'it':
- case 'ku':
- case 'lb':
- case 'ml':
- case 'mn':
- case 'mr':
- case 'nah':
- case 'nb':
- case 'ne':
- case 'nl':
- case 'nn':
- case 'no':
- case 'om':
- case 'or':
- case 'pa':
- case 'pap':
- case 'ps':
- case 'pt':
- case 'so':
- case 'sq':
- case 'sv':
- case 'sw':
- case 'ta':
- case 'te':
- case 'tk':
- case 'ur':
- case 'zu':
- return ($number == 1) ? 0 : 1;
-
- case 'am':
- case 'bh':
- case 'fil':
- case 'fr':
- case 'gun':
- case 'hi':
- case 'ln':
- case 'mg':
- case 'nso':
- case 'xbr':
- case 'ti':
- case 'wa':
- return (($number == 0) || ($number == 1)) ? 0 : 1;
-
- case 'be':
- case 'bs':
- case 'hr':
- case 'ru':
- case 'sr':
- case 'uk':
- return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
-
- case 'cs':
- case 'sk':
- return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
-
- case 'ga':
- return ($number == 1) ? 0 : (($number == 2) ? 1 : 2);
-
- case 'lt':
- return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
-
- case 'sl':
- return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3));
-
- case 'mk':
- return ($number % 10 == 1) ? 0 : 1;
-
- case 'mt':
- return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
-
- case 'lv':
- return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2);
-
- case 'pl':
- return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
-
- case 'cy':
- return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3));
-
- case 'ro':
- return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
-
- case 'ar':
- return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number >= 3) && ($number <= 10)) ? 3 : ((($number >= 11) && ($number <= 99)) ? 4 : 5))));
-
- default:
- return 0;
- }
- }
-
- /**
- * Set's a new plural rule
- *
- * @param string $rule Callback which acts as rule
- * @param string $locale Locale which is used for this callback
- * @return null
- */
- public static function setPlural($rule, $locale)
- {
- if ($locale == "pt_BR") {
- // temporary set a locale for brasilian
- $locale = "xbr";
- }
-
- if (strlen($locale) > 3) {
- $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
- }
-
- if (!is_callable($rule)) {
- require_once 'Zend/Translate/Exception.php';
- throw new Zend_Translate_Exception('The given rule can not be called');
- }
-
- self::$_plural[$locale] = $rule;
- }
-}
diff --git a/thirdparty/php-peg/Parser.php b/thirdparty/php-peg/Parser.php
index e41dedce567..ae6b4aefaa9 100644
--- a/thirdparty/php-peg/Parser.php
+++ b/thirdparty/php-peg/Parser.php
@@ -45,6 +45,26 @@ function match() {
* for result construction and building
*/
class Parser {
+ /**
+ * @var string
+ */
+ public $string;
+
+ /**
+ * @var int
+ */
+ public $pos;
+
+ /**
+ * @var int
+ */
+ public $depth;
+
+ /**
+ * @var array
+ */
+ public $regexps;
+
function __construct( $string ) {
$this->string = $string ;
$this->pos = 0 ;
@@ -105,7 +125,7 @@ function packhas( $key, $pos ) {
}
function packread( $key, $pos ) {
- throw 'PackRead after PackHas=>false in Parser.php' ;
+ throw new \Exception('PackRead after PackHas=>false in Parser.php');
}
function packwrite( $key, $pos, $res ) {