mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
881 lines
32 KiB
PHP
881 lines
32 KiB
PHP
<?php
|
|
|
|
namespace SilverStripe\i18n\Tests;
|
|
|
|
use SilverStripe\Assets\Filesystem;
|
|
use SilverStripe\Core\Config\Config;
|
|
use SilverStripe\Core\Manifest\ModuleLoader;
|
|
use SilverStripe\Dev\SapphireTest;
|
|
use SilverStripe\i18n\i18n;
|
|
use SilverStripe\i18n\Messages\YamlWriter;
|
|
use SilverStripe\i18n\Tests\i18nTest\TestDataObject;
|
|
use SilverStripe\i18n\Tests\i18nTextCollectorTest\Collector;
|
|
use SilverStripe\i18n\TextCollection\i18nTextCollector;
|
|
use SilverStripe\Security\Member;
|
|
|
|
class i18nTextCollectorTest extends SapphireTest
|
|
{
|
|
use i18nTestManifest;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $alternateBaseSavePath = null;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->setupManifest();
|
|
|
|
$this->alternateBaseSavePath = TEMP_PATH . DIRECTORY_SEPARATOR . 'i18nTextCollectorTest_webroot';
|
|
Filesystem::makeFolder($this->alternateBaseSavePath);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if (is_dir($this->alternateBaseSavePath ?? '')) {
|
|
Filesystem::removeFolder($this->alternateBaseSavePath);
|
|
}
|
|
|
|
$this->tearDownManifest();
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function testConcatenationInEntityValues()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$module = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.CONCATENATED',
|
|
'Line 1 and ' .
|
|
'Line \'2\' and ' .
|
|
'Line "3"',
|
|
|
|
'Comment'
|
|
);
|
|
|
|
_t(
|
|
'Test.CONCATENATED2',
|
|
"Line \"4\" and " .
|
|
"Line 5");
|
|
PHP;
|
|
$this->assertEquals(
|
|
[
|
|
'Test.CONCATENATED' => [
|
|
'default' => "Line 1 and Line '2' and Line \"3\"",
|
|
'comment' => 'Comment'
|
|
],
|
|
'Test.CONCATENATED2' => "Line \"4\" and Line 5"
|
|
],
|
|
$c->collectFromCode($php, null, $module)
|
|
);
|
|
}
|
|
|
|
public function testCollectFromNewTemplateSyntaxUsingParserSubclass()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$c->setWarnOnEmptyDefault(false);
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$html = <<<SS
|
|
<% _t('Test.SINGLEQUOTE','Single Quote'); %>
|
|
<%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 %>
|
|
<%t SilverStripe\\TestModule\\i18nTestModule.INJECTIONS_10 "This string is namespaced" %>
|
|
<%t SilverStripe\\\\TestModule\\\\i18nTestModule.INJECTIONS_11 "Escaped namespaced string" %>
|
|
SS;
|
|
$c->collectFromTemplate($html, null, $mymodule);
|
|
|
|
$this->assertEquals(
|
|
[
|
|
'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'
|
|
],
|
|
'SilverStripe\\TestModule\\i18nTestModule.INJECTIONS_10' => 'This string is namespaced',
|
|
'SilverStripe\\TestModule\\i18nTestModule.INJECTIONS_11' => 'Escaped namespaced string'
|
|
],
|
|
$c->collectFromTemplate($html, null, $mymodule)
|
|
);
|
|
|
|
// Test warning is raised on empty default
|
|
$c->setWarnOnEmptyDefault(true);
|
|
$this->expectNotice();
|
|
$this->expectNoticeMessage('Missing localisation default for key i18nTestModule.INJECTIONS_3');
|
|
|
|
$c->collectFromTemplate($html, null, $mymodule);
|
|
}
|
|
|
|
public function testCollectFromTemplateSimple()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$html = <<<SS
|
|
<%t Test.SINGLEQUOTE 'Single Quote' %>
|
|
SS;
|
|
$this->assertEquals(
|
|
[ 'Test.SINGLEQUOTE' => 'Single Quote' ],
|
|
$c->collectFromTemplate($html, null, $mymodule)
|
|
);
|
|
|
|
$html = <<<SS
|
|
<%t Test.DOUBLEQUOTE "Double Quote and Spaces" %>
|
|
SS;
|
|
$this->assertEquals(
|
|
[ 'Test.DOUBLEQUOTE' => "Double Quote and Spaces" ],
|
|
$c->collectFromTemplate($html, null, $mymodule)
|
|
);
|
|
|
|
$html = <<<SS
|
|
<%t Test.NOSEMICOLON "No Semicolon" %>
|
|
SS;
|
|
$this->assertEquals(
|
|
[ 'Test.NOSEMICOLON' => "No Semicolon" ],
|
|
$c->collectFromTemplate($html, null, $mymodule)
|
|
);
|
|
}
|
|
|
|
public function testCollectFromTemplateAdvanced()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$c->setWarnOnEmptyDefault(false);
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$html = <<<SS
|
|
<%t Test.PRIOANDCOMMENT ' Prio and Value with "Double Quotes"' is 'Comment with "Double Quotes"' %>
|
|
SS;
|
|
$this->assertEquals(
|
|
[ 'Test.PRIOANDCOMMENT' => [
|
|
'default' => ' Prio and Value with "Double Quotes"',
|
|
'comment' => 'Comment with "Double Quotes"',
|
|
]],
|
|
$c->collectFromTemplate($html, 'Test', $mymodule)
|
|
);
|
|
|
|
$html = <<<SS
|
|
<%t Test.PRIOANDCOMMENT " Prio and Value with 'Single Quotes'" is "Comment with 'Single Quotes'" %>
|
|
SS;
|
|
$this->assertEquals(
|
|
[ 'Test.PRIOANDCOMMENT' => [
|
|
'default' => " Prio and Value with 'Single Quotes'",
|
|
'comment' => "Comment with 'Single Quotes'",
|
|
]],
|
|
$c->collectFromTemplate($html, 'Test', $mymodule)
|
|
);
|
|
|
|
// Test empty
|
|
$html = <<<SS
|
|
<%t Test.PRIOANDCOMMENT %>
|
|
SS;
|
|
$this->assertEquals(
|
|
[],
|
|
$c->collectFromTemplate($html, null, $mymodule)
|
|
);
|
|
|
|
// Test warning is raised on empty default
|
|
$c->setWarnOnEmptyDefault(true);
|
|
$this->expectNotice();
|
|
$this->expectNoticeMessage('Missing localisation default for key Test.PRIOANDCOMMENT');
|
|
|
|
$c->collectFromTemplate($html, 'Test', $mymodule);
|
|
}
|
|
|
|
|
|
public function testCollectFromCodeSimple()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$php = <<<PHP
|
|
_t('Test.SINGLEQUOTE','Single Quote');
|
|
PHP;
|
|
$this->assertEquals(
|
|
[ 'Test.SINGLEQUOTE' => 'Single Quote' ],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
|
|
$php = <<<PHP
|
|
_t( "Test.DOUBLEQUOTE", "Double Quote and Spaces" );
|
|
PHP;
|
|
$this->assertEquals(
|
|
[ 'Test.DOUBLEQUOTE' => "Double Quote and Spaces" ],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
}
|
|
|
|
public function testCollectFromCodeAdvanced()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.NEWLINES',
|
|
'New Lines'
|
|
);
|
|
PHP;
|
|
$this->assertEquals(
|
|
[ 'Test.NEWLINES' => "New Lines" ],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.PRIOANDCOMMENT',
|
|
' Value with "Double Quotes"',
|
|
|
|
'Comment with "Double Quotes"'
|
|
);
|
|
PHP;
|
|
$this->assertEquals(
|
|
[
|
|
'Test.PRIOANDCOMMENT' => [
|
|
'default' => ' Value with "Double Quotes"',
|
|
'comment' => 'Comment with "Double Quotes"',
|
|
]
|
|
],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.PRIOANDCOMMENT',
|
|
" Value with 'Single Quotes'",
|
|
|
|
"Comment with 'Single Quotes'"
|
|
);
|
|
PHP;
|
|
$this->assertEquals(
|
|
[ 'Test.PRIOANDCOMMENT' => [
|
|
'default' => " Value with 'Single Quotes'",
|
|
'comment' => "Comment with 'Single Quotes'"
|
|
] ],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.PRIOANDCOMMENT',
|
|
'Value with \'Escaped Single Quotes\''
|
|
);
|
|
PHP;
|
|
$this->assertEquals(
|
|
[ 'Test.PRIOANDCOMMENT' => "Value with 'Escaped Single Quotes'" ],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.PRIOANDCOMMENT',
|
|
"Doublequoted Value with 'Unescaped Single Quotes'"
|
|
|
|
|
|
);
|
|
PHP;
|
|
$this->assertEquals(
|
|
[ 'Test.PRIOANDCOMMENT' => "Doublequoted Value with 'Unescaped Single Quotes'"],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
}
|
|
|
|
public function testCollectFromCodeNamespace()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
$php = <<<PHP
|
|
<?php
|
|
namespace SilverStripe\Framework\Core;
|
|
|
|
use SilverStripe\ORM\DataObject;
|
|
|
|
class MyClass extends Base implements SomeService {
|
|
public function getNewLines(\$class) {
|
|
if (
|
|
!is_subclass_of(\$class, DataObject::class)
|
|
|| !Object::has_extension(\$class, \SilverStripe\Versioned\Versioned::class)
|
|
) {
|
|
return null;
|
|
}
|
|
return _t(
|
|
__CLASS__.'.NEWLINES',
|
|
'New Lines'
|
|
);
|
|
}
|
|
public function getAnotherString() {
|
|
return _t(
|
|
'SilverStripe\\\\Framework\\\\MyClass.ANOTHER_STRING',
|
|
'Slash=\\\\, Quote=\\''
|
|
);
|
|
}
|
|
public function getDoubleQuotedString() {
|
|
return _t(
|
|
"SilverStripe\\\\Framework\\\\MyClass.DOUBLE_STRING",
|
|
"Slash=\\\\, Quote=\\""
|
|
);
|
|
}
|
|
public function getMagicConstantStringFromSelf()
|
|
{
|
|
return _t(
|
|
self::class . '.SELF_CLASS',
|
|
'Self Class'
|
|
);
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$this->assertEquals(
|
|
[
|
|
'SilverStripe\\Framework\\Core\\MyClass.NEWLINES' => "New Lines",
|
|
'SilverStripe\\Framework\\MyClass.ANOTHER_STRING' => 'Slash=\\, Quote=\'',
|
|
'SilverStripe\\Framework\\MyClass.DOUBLE_STRING' => 'Slash=\\, Quote="',
|
|
'SilverStripe\\Framework\\Core\\MyClass.SELF_CLASS' => 'Self Class',
|
|
],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
}
|
|
|
|
public function testCollectFromClassSyntax()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
$php = <<<PHP
|
|
<?php
|
|
namespace SilverStripe\Framework\Core;
|
|
|
|
use SilverStripe\ORM\DataObject;
|
|
use SilverStripe\Versioned\Versioned;
|
|
use Some\Space\MyClass as AliasClass;
|
|
use NoNamespaceClass;
|
|
|
|
class MyClass extends Base implements SomeService {
|
|
public function pointlessFunction1(\$class) {
|
|
if (
|
|
!is_subclass_of(\$class, DataObject::class)
|
|
|| !Object::has_extension(\$class, Versioned::class)
|
|
) {
|
|
return null;
|
|
}
|
|
return _t(
|
|
Versioned::class . '.OTHER_NAMESPACE',
|
|
'New Lines'
|
|
);
|
|
}
|
|
public function pointlessFunction2() {
|
|
return _t(
|
|
SameNamespaceClass::class . '.SAME_NAMESPACE',
|
|
'Slash=\\\\, Quote=\\''
|
|
);
|
|
}
|
|
public function pointlessFunction3() {
|
|
return _t(
|
|
AliasClass::class . ".ALIAS_CLASS",
|
|
"Slash=\\\\, Quote=\\""
|
|
);
|
|
}
|
|
public function pointlessFunction4()
|
|
{
|
|
return _t(
|
|
NoNamespaceClass::class . '.NO_NAMESPACE',
|
|
'Self Class'
|
|
);
|
|
}
|
|
}
|
|
PHP;
|
|
|
|
$this->assertEquals(
|
|
[
|
|
'SilverStripe\\Versioned\\Versioned.OTHER_NAMESPACE' => "New Lines",
|
|
'SilverStripe\\Framework\\Core\\SameNamespaceClass.SAME_NAMESPACE' => 'Slash=\\, Quote=\'',
|
|
'Some\\Space\\MyClass.ALIAS_CLASS' => 'Slash=\\, Quote="',
|
|
'NoNamespaceClass.NO_NAMESPACE' => 'Self Class',
|
|
],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
}
|
|
|
|
public function testNewlinesInEntityValues()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.NEWLINESINGLEQUOTE',
|
|
'Line 1
|
|
Line 2'
|
|
);
|
|
PHP;
|
|
|
|
$eol = PHP_EOL;
|
|
$this->assertEquals(
|
|
[ 'Test.NEWLINESINGLEQUOTE' => "Line 1{$eol}Line 2" ],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
|
|
$php = <<<PHP
|
|
_t(
|
|
'Test.NEWLINEDOUBLEQUOTE',
|
|
"Line 1
|
|
Line 2"
|
|
);
|
|
PHP;
|
|
$this->assertEquals(
|
|
[ 'Test.NEWLINEDOUBLEQUOTE' => "Line 1{$eol}Line 2" ],
|
|
$c->collectFromCode($php, null, $mymodule)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test extracting entities from the new _t method signature
|
|
*/
|
|
public function testCollectFromCodeNewSignature()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$c->setWarnOnEmptyDefault(false); // Disable warnings for tests
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$php = <<<PHP
|
|
_t('i18nTestModule.NEWMETHODSIG',"New _t method signature test");
|
|
_t('i18nTestModule.INJECTIONS2', "Hello {name} {greeting}. But it is late, {goodbye}",
|
|
["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)",
|
|
["name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"]);
|
|
_t('i18nTestModule.INJECTIONS4', ["name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow"]);
|
|
_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, null, $mymodule);
|
|
|
|
$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->expectNotice();
|
|
$this->expectNoticeMessage('Missing localisation default for key i18nTestModule.INJECTIONS4');
|
|
|
|
$php = <<<PHP
|
|
_t('i18nTestModule.INJECTIONS4', ["name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow"]);
|
|
PHP;
|
|
$c->setWarnOnEmptyDefault(true);
|
|
$c->collectFromCode($php, null, $mymodule);
|
|
}
|
|
|
|
public function testUncollectableCode()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$php = <<<PHP
|
|
_t(static::class.'.KEY1', 'Default');
|
|
_t(parent::class.'.KEY1', 'Default');
|
|
_t('Collectable.KEY4', 'Default');
|
|
PHP;
|
|
|
|
$collectedTranslatables = $c->collectFromCode($php, null, $mymodule);
|
|
|
|
// Only one item is collectable
|
|
$expectedArray = [ 'Collectable.KEY4' => 'Default' ];
|
|
$this->assertEquals($expectedArray, $collectedTranslatables);
|
|
}
|
|
|
|
public function testCollectFromIncludedTemplates()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$c->setWarnOnEmptyDefault(false); // Disable warnings for tests
|
|
$mymodule = ModuleLoader::inst()->getManifest()->getModule('i18ntestmodule');
|
|
|
|
$templateFilePath = $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss';
|
|
$html = file_get_contents($templateFilePath ?? '');
|
|
$matches = $c->collectFromTemplate($html, $templateFilePath, $mymodule);
|
|
|
|
$this->assertArrayHasKey('i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE', $matches);
|
|
$this->assertEquals(
|
|
'Layout Template no namespace',
|
|
$matches['i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE']
|
|
);
|
|
$this->assertArrayHasKey('i18nTestModule.ss.REPLACEMENTNONAMESPACE', $matches);
|
|
$this->assertEquals(
|
|
'My replacement no namespace: {replacement}',
|
|
$matches['i18nTestModule.ss.REPLACEMENTNONAMESPACE']
|
|
);
|
|
$this->assertArrayHasKey('i18nTestModule.LAYOUTTEMPLATE', $matches);
|
|
$this->assertEquals(
|
|
'Layout Template',
|
|
$matches['i18nTestModule.LAYOUTTEMPLATE']
|
|
);
|
|
$this->assertArrayHasKey('i18nTestModule.REPLACEMENTNAMESPACE', $matches);
|
|
$this->assertEquals(
|
|
'My replacement: {replacement}',
|
|
$matches['i18nTestModule.REPLACEMENTNAMESPACE']
|
|
);
|
|
|
|
// Includes should not automatically inject translations into parent templates
|
|
$this->assertArrayNotHasKey('i18nTestModule.WITHNAMESPACE', $matches);
|
|
$this->assertArrayNotHasKey('i18nTestModuleInclude_ss.NONAMESPACE', $matches);
|
|
$this->assertArrayNotHasKey('i18nTestModuleInclude_ss.REPLACEMENTINCLUDENAMESPACE', $matches);
|
|
$this->assertArrayNotHasKey('i18nTestModuleInclude_ss.REPLACEMENTINCLUDENONAMESPACE', $matches);
|
|
}
|
|
|
|
public function testCollectMergesWithExisting()
|
|
{
|
|
$c = i18nTextCollector::create();
|
|
$c->setWarnOnEmptyDefault(false);
|
|
$c->setWriter(new YamlWriter());
|
|
$c->basePath = $this->alternateBasePath;
|
|
$c->baseSavePath = $this->alternateBaseSavePath;
|
|
|
|
$entitiesByModule = $c->collect(null, true /* merge */);
|
|
$this->assertArrayHasKey(
|
|
'i18nTestModule.ENTITY',
|
|
$entitiesByModule['i18ntestmodule'],
|
|
'Retains existing entities'
|
|
);
|
|
$this->assertArrayHasKey(
|
|
'i18nTestModule.NEWENTITY',
|
|
$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()
|
|
{
|
|
i18n::set_locale('en_US'); //set the locale to the US locale expected in the asserts
|
|
i18n::config()->set('default_locale', 'en_US');
|
|
i18n::config()->set('missing_default_warning', false);
|
|
|
|
$c = i18nTextCollector::create();
|
|
$c->setWarnOnEmptyDefault(false);
|
|
$c->setWriter(new YamlWriter());
|
|
$c->basePath = $this->alternateBasePath;
|
|
$c->baseSavePath = $this->alternateBaseSavePath;
|
|
|
|
$c->run();
|
|
|
|
// i18ntestmodule
|
|
$moduleLangFile = "{$this->alternateBaseSavePath}/i18ntestmodule/lang/" . $c->getDefaultLocale() . '.yml';
|
|
$this->assertTrue(
|
|
file_exists($moduleLangFile ?? ''),
|
|
'Master language file can be written to modules /lang folder'
|
|
);
|
|
|
|
$moduleLangFileContent = file_get_contents($moduleLangFile ?? '');
|
|
$this->assertStringContainsString(
|
|
" ADDITION: Addition\n",
|
|
$moduleLangFileContent
|
|
);
|
|
$this->assertStringContainsString(
|
|
" ENTITY: 'Entity with \"Double Quotes\"'\n",
|
|
$moduleLangFileContent
|
|
);
|
|
$this->assertStringContainsString(
|
|
" MAINTEMPLATE: 'Main Template'\n",
|
|
$moduleLangFileContent
|
|
);
|
|
$this->assertStringContainsString(
|
|
" OTHERENTITY: 'Other Entity'\n",
|
|
$moduleLangFileContent
|
|
);
|
|
$this->assertStringContainsString(
|
|
" WITHNAMESPACE: 'Include Entity with Namespace'\n",
|
|
$moduleLangFileContent
|
|
);
|
|
$this->assertStringContainsString(
|
|
" NONAMESPACE: 'Include Entity without Namespace'\n",
|
|
$moduleLangFileContent
|
|
);
|
|
|
|
// i18nothermodule
|
|
$otherModuleLangFile = "{$this->alternateBaseSavePath}/i18nothermodule/lang/" . $c->getDefaultLocale() . '.yml';
|
|
$this->assertTrue(
|
|
file_exists($otherModuleLangFile ?? ''),
|
|
'Master language file can be written to modules /lang folder'
|
|
);
|
|
$otherModuleLangFileContent = file_get_contents($otherModuleLangFile ?? '');
|
|
$this->assertStringContainsString(
|
|
" ENTITY: 'Other Module Entity'\n",
|
|
$otherModuleLangFileContent
|
|
);
|
|
$this->assertStringContainsString(
|
|
" MAINTEMPLATE: 'Main Template Other Module'\n",
|
|
$otherModuleLangFileContent
|
|
);
|
|
}
|
|
|
|
public function testCollectFromEntityProvidersInCustomObject()
|
|
{
|
|
// 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(
|
|
[
|
|
'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(
|
|
[
|
|
'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']
|
|
);
|
|
}
|
|
|
|
public function testCollectFromORM()
|
|
{
|
|
// note: Disable _fakewebroot manifest for this test
|
|
$this->popManifests();
|
|
|
|
// update config because test can't load config from actual class
|
|
Config::inst()->update(TestDataObject::class, 'db', [
|
|
'MyProperty' => 'Varchar',
|
|
'MyUntranslatedProperty' => 'Text'
|
|
]);
|
|
Config::inst()->update(TestDataObject::class, 'has_one', [
|
|
'HasOneRelation' => Member::class
|
|
]);
|
|
Config::inst()->update(TestDataObject::class, 'has_many', [
|
|
'HasManyRelation' => Member::class
|
|
]);
|
|
Config::inst()->update(TestDataObject::class, 'many_many', [
|
|
'ManyManyRelation' => Member::class
|
|
]);
|
|
|
|
$c = i18nTextCollector::create();
|
|
|
|
// Collect from MyObject.php
|
|
$filePath = __DIR__ . '/i18nTest/TestDataObject.php';
|
|
$matches = $c->collectFromORM($filePath);
|
|
$this->assertEquals(
|
|
[
|
|
'SilverStripe\i18n\Tests\i18nTest\TestDataObject.db_MyProperty' => 'My property',
|
|
'SilverStripe\i18n\Tests\i18nTest\TestDataObject.db_MyUntranslatedProperty' => 'My untranslated property',
|
|
'SilverStripe\i18n\Tests\i18nTest\TestDataObject.has_one_HasOneRelation' => 'Has one relation',
|
|
'SilverStripe\i18n\Tests\i18nTest\TestDataObject.has_many_HasManyRelation' => 'Has many relation',
|
|
'SilverStripe\i18n\Tests\i18nTest\TestDataObject.many_many_ManyManyRelation' => 'Many many relation',
|
|
],
|
|
$matches
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test that duplicate keys are resolved to the appropriate modules
|
|
*/
|
|
public function testResolveDuplicates()
|
|
{
|
|
$collector = new Collector();
|
|
|
|
// Dummy data as collected
|
|
$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 = [
|
|
'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(
|
|
['i18ntestmodule.THREE', 'i18ntestmodule.TWO'],
|
|
$conflictsA
|
|
);
|
|
|
|
// Removing module3 should remove a conflict
|
|
unset($data2['module3']);
|
|
$conflictsB = $collector->getConflicts_Test($data2);
|
|
$this->assertEquals(
|
|
['i18ntestmodule.THREE'],
|
|
$conflictsB
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test ability for textcollector to detect modules
|
|
*/
|
|
public function testModuleDetection()
|
|
{
|
|
$collector = new Collector();
|
|
$modules = ModuleLoader::inst()->getManifest()->getModules();
|
|
$this->assertEquals(
|
|
[
|
|
'i18nnonstandardmodule',
|
|
'i18nothermodule',
|
|
'i18ntestmodule',
|
|
],
|
|
array_keys($modules ?? [])
|
|
);
|
|
|
|
$this->assertEquals('i18ntestmodule', $collector->findModuleForClass_Test('i18nTestNamespacedClass'));
|
|
$this->assertEquals(
|
|
'i18ntestmodule',
|
|
$collector->findModuleForClass_Test('i18nTest\\i18nTestNamespacedClass')
|
|
);
|
|
$this->assertEquals('i18ntestmodule', $collector->findModuleForClass_Test('i18nTestSubModule'));
|
|
}
|
|
|
|
/**
|
|
* Test that text collector can detect module file lists properly
|
|
*/
|
|
public function testModuleFileList()
|
|
{
|
|
$collector = new Collector();
|
|
$collector->basePath = $this->alternateBasePath;
|
|
$collector->baseSavePath = $this->alternateBaseSavePath;
|
|
|
|
// Non-standard modules can't be safely filtered, so just index everything
|
|
$nonStandardFiles = $collector->getFileListForModule_Test('i18nnonstandardmodule');
|
|
$nonStandardRoot = $this->alternateBasePath . '/i18nnonstandardmodule';
|
|
$this->assertEquals(3, count($nonStandardFiles ?? []));
|
|
$this->assertArrayHasKey("{$nonStandardRoot}/_config.php", $nonStandardFiles);
|
|
$this->assertArrayHasKey("{$nonStandardRoot}/phpfile.php", $nonStandardFiles);
|
|
$this->assertArrayHasKey("{$nonStandardRoot}/template.ss", $nonStandardFiles);
|
|
|
|
// Normal module should have predictable dir structure
|
|
$testFiles = $collector->getFileListForModule_Test('i18ntestmodule');
|
|
$testRoot = $this->alternateBasePath . '/i18ntestmodule';
|
|
$this->assertEquals(7, count($testFiles ?? []));
|
|
// Code in code folder is detected
|
|
$this->assertArrayHasKey("{$testRoot}/code/i18nTestModule.php", $testFiles);
|
|
$this->assertArrayHasKey("{$testRoot}/code/subfolder/_config.php", $testFiles);
|
|
$this->assertArrayHasKey("{$testRoot}/code/subfolder/i18nTestSubModule.php", $testFiles);
|
|
$this->assertArrayHasKey("{$testRoot}/code/subfolder/i18nTestNamespacedClass.php", $testFiles);
|
|
// Templates in templates folder is detected
|
|
$this->assertArrayHasKey("{$testRoot}/templates/Includes/i18nTestModuleInclude.ss", $testFiles);
|
|
$this->assertArrayHasKey("{$testRoot}/templates/Layout/i18nTestModule.ss", $testFiles);
|
|
$this->assertArrayHasKey("{$testRoot}/templates/i18nTestModule.ss", $testFiles);
|
|
|
|
// 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(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);
|
|
}
|
|
}
|