mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
BUGFIX Added SSViwer support for i18n namespaces in templates with <% _t('MyNamespace.MyEntity', ... %>, to work around magically added namespaces from the parsed template file. Those auto-namespaces were logically not working in includes, as the parsing context is always the including template. Legacy support for auto-namespaces is still present due to its high usage.
ENHANCEMENT Added unit tests for i18n template parsing git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@65361 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
fdeaececb1
commit
728e691a1a
@ -433,14 +433,20 @@ class SSViewer extends Object {
|
||||
$content = ereg_replace('<' . '% +else +%' . '>', '<? } else { ?>', $content);
|
||||
$content = ereg_replace('<' . '% +end_if +%' . '>', '<? } ?>', $content);
|
||||
|
||||
// i18n
|
||||
// i18n - get filename of currently parsed template
|
||||
// CAUTION: No spaces allowed between arguments for all i18n calls!
|
||||
ereg('.*[\/](.*)',$template,$path);
|
||||
$content = ereg_replace('<' . '% +_t\((\'([^\']*)\'|"([^"]*)")(([^)]|\)[^ ]|\) +[^% ])*)\) +%' . '>', '<?= _t(\''. $path[1] . '.\\2\\3\'\\4) ?>', $content);
|
||||
|
||||
// i18n - sprintf => "sprintf(_t(...),$argument)"
|
||||
// CAUTION: No spaces allowed between arguments!
|
||||
$content = ereg_replace('<' . '% +sprintf\(_t\((\'([^\']*)\'|"([^"]*)")(([^)]|\)[^ ]|\) +[^% ])*)\),\<\?= +([^\?]*) +\?\>) +%' . '>', '<?= sprintf(_t(\''. $path[1] . '.\\2\\3\'\\4),\\6) ?>', $content);
|
||||
|
||||
// i18n _t(...) - with entity only (no dots in namespace), meaning the current template filename will be added as a namespace
|
||||
$content = ereg_replace('<' . '% +_t\((\'([^\.\']*)\'|"([^\."]*)")(([^)]|\)[^ ]|\) +[^% ])*)\) +%' . '>', '<?= _t(\''. $path[1] . '.\\2\\3\'\\4) ?>', $content);
|
||||
// i18n _t(...)
|
||||
$content = ereg_replace('<' . '% +_t\((\'([^\']*)\'|"([^"]*)")(([^)]|\)[^ ]|\) +[^% ])*)\) +%' . '>', '<?= _t(\'\\2\\3\'\\4) ?>', $content);
|
||||
|
||||
// i18n sprintf(_t(...),$argument) with entity only (no dots in namespace), meaning the current template filename will be added as a namespace
|
||||
$content = ereg_replace('<' . '% +sprintf\(_t\((\'([^\.\']*)\'|"([^\."]*)")(([^)]|\)[^ ]|\) +[^% ])*)\),\<\?= +([^\?]*) +\?\>) +%' . '>', '<?= sprintf(_t(\''. $path[1] . '.\\2\\3\'\\4),\\6) ?>', $content);
|
||||
// i18n sprintf(_t(...),$argument)
|
||||
$content = ereg_replace('<' . '% +sprintf\(_t\((\'([^\']*)\'|"([^"]*)")(([^)]|\)[^ ]|\) +[^% ])*)\),\<\?= +([^\?]*) +\?\>) +%' . '>', '<?= sprintf(_t(\'\\2\\3\'\\4),\\6) ?>', $content);
|
||||
|
||||
// </base> isnt valid html? !?
|
||||
$content = ereg_replace('<' . '% +base_tag +%' . '>', '<base href="<?= Director::absoluteBaseURL(); ?>" />', $content);
|
||||
|
||||
|
@ -1,8 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Base-class for storage and retrieval of translated entities.
|
||||
* Most common use is translation of the CMS-interface through the _t()-method
|
||||
* (in controller/model) and the <% _t() %> template variable.
|
||||
*
|
||||
* Usage PHP:
|
||||
* <code>
|
||||
* _t('MyNamespace.MYENTITY', 'My default natural language value');
|
||||
* _t('MyNamespace.MYENTITY', 'My default natural language value', PR_MEDIUM, 'My explanatory context');
|
||||
* sprintf(_t('MyNamespace.MYENTITY', 'Counting %s things'), 42);
|
||||
* </code>
|
||||
*
|
||||
* Usage Templates:
|
||||
* <code>
|
||||
* <% _t('MyNamespace.MYENTITY', 'My default natural language value') %>
|
||||
* <% sprintf(_t('MyNamespace.MYENTITY','Counting %s things'),$ThingsCount) %>
|
||||
* </code>
|
||||
*
|
||||
* Usage Javascript (see sapphire/javascript/i18n.js):
|
||||
* <code>
|
||||
* ss.i18n._t('MyEntity.MyNamespace','My default natural language value');
|
||||
* </code>
|
||||
*
|
||||
* File-based i18n-translations always have a "locale" (e.g. 'en_US').
|
||||
* Common language names (e.g. 'en') are mainly used in {Translatable} for
|
||||
|
@ -1,2 +1,4 @@
|
||||
<% _t("i18nTestModule.WITHNAMESPACE", 'Include Entity with Namespace') %>
|
||||
<% _t("NONAMESPACE", 'Include Entity without Namespace') %>
|
||||
<% _t("NONAMESPACE", 'Include Entity without Namespace') %>
|
||||
<% sprintf(_t('i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE','My include replacement: %s'),$TestProperty) %>
|
||||
<% sprintf(_t('SPRINTFINCLUDENONAMESPACE','My include replacement no namespace: %s'),$TestProperty) %>
|
@ -1,2 +1,5 @@
|
||||
<% _t('i18nTestModule.LAYOUTTEMPLATE',"Layout Template")%>
|
||||
<% _t('i18nTestModule.LAYOUTTEMPLATE',"Layout Template") %>
|
||||
<% _t('LAYOUTTEMPLATENONAMESPACE',"Layout Template no namespace") %>
|
||||
<% sprintf(_t('i18nTestModule.SPRINTFNAMESPACE','My replacement: %s'),$TestProperty) %>
|
||||
<% sprintf(_t('SPRINTFNONAMESPACE','My replacement no namespace: %s'),$TestProperty) %>
|
||||
<% include i18nTestModuleInclude %>
|
@ -1,2 +1,3 @@
|
||||
<% _t('i18nTestModule.MAINTEMPLATE',"Main Template")%>
|
||||
<% _t('i18nTestModule.MAINTEMPLATE',"Main Template") %>
|
||||
$Layout
|
||||
lonely _t() call that should be ignored
|
@ -4,6 +4,64 @@
|
||||
* @subpackage tests
|
||||
*/
|
||||
class i18nTest extends SapphireTest {
|
||||
|
||||
/**
|
||||
* @var string $tmpBasePath Used to write language files.
|
||||
* We don't want to store them inside sapphire (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;
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->alternateBasePath = Director::baseFolder() . "/sapphire/tests/i18n/_fakewebroot";
|
||||
$this->alternateBaseSavePath = TEMP_FOLDER . '/i18nTextCollectorTest_webroot';
|
||||
FileSystem::makeFolder($this->alternateBaseSavePath);
|
||||
|
||||
// SSViewer and ManifestBuilder don't support different webroots, hence we set the paths manually
|
||||
global $_CLASS_MANIFEST;
|
||||
$_CLASS_MANIFEST['i18nTestModule'] = $this->alternateBasePath . '/i18ntestmodule/code/i18nTestModule.php';
|
||||
$_CLASS_MANIFEST['i18nTestModule_Addition'] = $this->alternateBasePath . '/i18ntestmodule/code/i18nTestModule.php';
|
||||
$_CLASS_MANIFEST['i18nTestModuleDecorator'] = $this->alternateBasePath . '/i18nothermodule/code/i18nTestModuleDecorator.php';
|
||||
|
||||
global $_ALL_CLASSES;
|
||||
$_ALL_CLASSES['parents']['i18nTestModule'] = array('DataObject'=>'DataObject','Object'=>'Object');
|
||||
$_ALL_CLASSES['parents']['i18nTestModule_Addition'] = array('Object'=>'Object');
|
||||
$_ALL_CLASSES['parents']['i18nTestModuleDecorator'] = array('DataObjectDecorator'=>'DataObjectDecorator','Object'=>'Object');
|
||||
|
||||
global $_TEMPLATE_MANIFEST;
|
||||
$_TEMPLATE_MANIFEST['i18nTestModule.ss'] = array(
|
||||
'main' => $this->alternateBasePath . '/i18ntestmodule/templates/i18nTestModule.ss',
|
||||
'Layout' => $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss',
|
||||
);
|
||||
$_TEMPLATE_MANIFEST['i18nTestModuleInclude.ss'] = array(
|
||||
'Includes' => $this->alternateBasePath . '/i18ntestmodule/templates/Includes/i18nTestModuleInclude.ss',
|
||||
);
|
||||
$_TEMPLATE_MANIFEST['i18nTestModule.ss'] = array(
|
||||
'main' => $this->alternateBasePath . '/i18ntestmodule/templates/i18nTestModule.ss',
|
||||
'Layout' => $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss',
|
||||
);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
//FileSystem::removeFolder($this->tmpBasePath);
|
||||
|
||||
global $_CLASS_MANIFEST;
|
||||
unset($_CLASS_MANIFEST['i18nTestModule']);
|
||||
unset($_CLASS_MANIFEST['i18nTestModule_Addition']);
|
||||
|
||||
global $_TEMPLATE_MANIFEST;
|
||||
unset($_TEMPLATE_MANIFEST['i18nTestModule.ss']);
|
||||
unset($_TEMPLATE_MANIFEST['i18nTestModuleInclude.ss']);
|
||||
}
|
||||
|
||||
function testGetExistingTranslations() {
|
||||
$translations = i18n::get_existing_translations();
|
||||
$this->assertTrue(isset($translations['en_US']), 'Checking for en_US translation');
|
||||
@ -64,6 +122,78 @@ class i18nTest extends SapphireTest {
|
||||
);
|
||||
}
|
||||
|
||||
function testTemplateTranslation() {
|
||||
global $lang;
|
||||
$oldLocale = i18n::get_locale();
|
||||
|
||||
i18n::set_locale('en_US');
|
||||
$lang['en_US']['i18nTestModule']['MAINTEMPLATE'] = 'Main Template';
|
||||
$lang['en_US']['i18nTestModule.ss']['SPRINTFNONAMESPACE'] = 'My replacement no namespace: %s';
|
||||
$lang['en_US']['i18nTestModule']['LAYOUTTEMPLATE'] = 'Layout Template';
|
||||
$lang['en_US']['i18nTestModule.ss']['LAYOUTTEMPLATENONAMESPACE'] = 'Layout Template no namespace';
|
||||
$lang['en_US']['i18nTestModule']['SPRINTFNAMESPACE'] = 'My replacement: %s';
|
||||
$lang['en_US']['i18nTestModule']['WITHNAMESPACE'] = 'Include Entity with Namespace';
|
||||
$lang['en_US']['i18nTestModule.ss']['NONAMESPACE'] = 'Include Entity without Namespace';
|
||||
$lang['en_US']['i18nTestModuleInclude.ss']['SPRINTFINCLUDENAMESPACE'] = 'My include replacement: %s';
|
||||
$lang['en_US']['i18nTestModule.ss']['SPRINTFINCLUDENONAMESPACE'] = 'My include replacement no namespace: %s';
|
||||
$viewer = new SSViewer('i18nTestModule');
|
||||
$parsedHtml = $viewer->process(new ArrayData(array('TestProperty' => 'TestPropertyValue')));
|
||||
$this->assertContains(
|
||||
"Layout Template\n",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"Layout Template no namespace\n",
|
||||
$parsedHtml
|
||||
);
|
||||
|
||||
i18n::set_locale('de_DE');
|
||||
$lang['de_DE']['i18nTestModule']['MAINTEMPLATE'] = 'TRANS Main Template';
|
||||
$lang['de_DE']['i18nTestModule.ss']['SPRINTFNONAMESPACE'] = 'TRANS My replacement no namespace: %s';
|
||||
$lang['de_DE']['i18nTestModule']['LAYOUTTEMPLATE'] = 'TRANS Layout Template';
|
||||
$lang['de_DE']['i18nTestModule.ss']['LAYOUTTEMPLATENONAMESPACE'] = 'TRANS Layout Template no namespace';
|
||||
$lang['de_DE']['i18nTestModule']['SPRINTFNAMESPACE'] = 'TRANS My replacement: %s';
|
||||
$lang['de_DE']['i18nTestModule']['WITHNAMESPACE'] = 'TRANS Include Entity with Namespace';
|
||||
$lang['de_DE']['i18nTestModule.ss']['NONAMESPACE'] = 'TRANS Include Entity without Namespace';
|
||||
$lang['de_DE']['i18nTestModuleInclude.ss']['SPRINTFINCLUDENAMESPACE'] = 'TRANS My include replacement: %s';
|
||||
$lang['de_DE']['i18nTestModule.ss']['SPRINTFINCLUDENONAMESPACE'] = 'TRANS My include replacement no namespace: %s';
|
||||
$viewer = new SSViewer('i18nTestModule');
|
||||
$parsedHtml = $viewer->process(new ArrayData(array('TestProperty' => 'TestPropertyValue')));
|
||||
$this->assertContains(
|
||||
"TRANS Main Template\n",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"TRANS Layout Template\n",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"TRANS Layout Template no namespace",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"TRANS My replacement: TestPropertyValue",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"TRANS Include Entity with Namespace",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"TRANS Include Entity without Namespace",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"TRANS My include replacement: TestPropertyValue",
|
||||
$parsedHtml
|
||||
);
|
||||
$this->assertContains(
|
||||
"TRANS My include replacement no namespace: TestPropertyValue",
|
||||
$parsedHtml
|
||||
);
|
||||
|
||||
i18n::set_locale($oldLocale);
|
||||
}
|
||||
}
|
||||
|
||||
class i18nTest_DataObject extends DataObject implements TestOnly {
|
||||
|
@ -330,13 +330,49 @@ PHP;
|
||||
|
||||
$templateFilePath = $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss';
|
||||
$html = file_get_contents($templateFilePath);
|
||||
$matches = $c->collectFromTemplate($html, 'mymodule', 'RandomNamespace');
|
||||
|
||||
/*
|
||||
$this->assertArrayHasKey('i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$c->collectFromTemplate($html, 'mymodule', 'RandomNamespace'),
|
||||
array(
|
||||
'i18nTestModule.WITHNAMESPACE' => array('Include Entity with Namespace', null, null),
|
||||
'i18nTestModuleInclude.ss.NONAMESPACE' => array('Include Entity without Namespace', null, null),
|
||||
'i18nTestModule.LAYOUTTEMPLATE' => array('Layout Template', null, null),
|
||||
)
|
||||
$matches['i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE'],
|
||||
array('Layout Template no namespace', null, null)
|
||||
);
|
||||
*/
|
||||
$this->assertArrayHasKey('RandomNamespace.SPRINTFNONAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['RandomNamespace.SPRINTFNONAMESPACE'],
|
||||
array('My replacement no namespace: %s', null, null)
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModule.LAYOUTTEMPLATE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModule.LAYOUTTEMPLATE'],
|
||||
array('Layout Template', null, null)
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModule.SPRINTFNAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModule.SPRINTFNAMESPACE'],
|
||||
array('My replacement: %s', null, null)
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModule.WITHNAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModule.WITHNAMESPACE'],
|
||||
array('Include Entity with Namespace', null, null)
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModuleInclude.ss.NONAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModuleInclude.ss.NONAMESPACE'],
|
||||
array('Include Entity without Namespace', null, null)
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE'],
|
||||
array('My include replacement: %s', null, null)
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE'],
|
||||
array('My include replacement no namespace: %s', null, null)
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user