mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
FEATURE Collect i18n Entities from dynamic PHP code - useful to translate statics. Added DataObject->provideI18nEntities() and i18nTextCollector->collectFromEntityProviders(). See #1625
FEATURE Making DataObject attributes translatable through i18n class, e.g. $db and all relation statics. Use DataObject->fieldLabels() to access translated attributes. ENHANCEMENT Ignoring entity-names with $ signs (most likely dynamic properties) in i18nTextCollector->collectFromCode() git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@64881 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
076fcde635
commit
84db28eb10
@ -92,7 +92,7 @@ class i18nTextCollector extends Object {
|
||||
if(substr($filePath,-3) == 'php') {
|
||||
$content = file_get_contents($filePath);
|
||||
$entitiesArr = array_merge($entitiesArr,(array)$this->collectFromCode($content, $module));
|
||||
$entitiesArr = array_merge($entitiesArr, (array)$this->collectFromStatics($filePath, $module));
|
||||
$entitiesArr = array_merge($entitiesArr, (array)$this->collectFromEntityProviders($filePath, $module));
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ class i18nTextCollector extends Object {
|
||||
fwrite($fh, "<?php\n\nglobal \$lang;\n\n" . $php . "\n?>");
|
||||
fclose($fh);
|
||||
|
||||
Debug::message("Created file: $langFolder/" . $this->defaultLocale . ".php", false);
|
||||
//Debug::message("Created file: $langFolder/" . $this->defaultLocale . ".php", false);
|
||||
} else {
|
||||
user_error("Cannot write language file! Please check permissions of $langFolder/" . $this->defaultLocale . ".php", E_USER_ERROR);
|
||||
}
|
||||
@ -247,6 +247,13 @@ class i18nTextCollector extends Object {
|
||||
$namespace = $_namespace;
|
||||
}
|
||||
|
||||
// If a dollar sign is used in the entity name,
|
||||
// we can't resolve without running the method,
|
||||
// and skip the processing. This is mostly used for
|
||||
// dynamically translating static properties, e.g. looping
|
||||
// through $db, which are detected by {@link collectFromEntityProviders}.
|
||||
if(strpos('$', $entity) !== FALSE) return false;
|
||||
|
||||
// remove wrapping quotes
|
||||
$value = ($regs[2]) ? substr($regs[2],1,-1) : null;
|
||||
|
||||
@ -309,14 +316,14 @@ class i18nTextCollector extends Object {
|
||||
return $php;
|
||||
}
|
||||
|
||||
function collectFromStatics($filePath) {
|
||||
function collectFromEntityProviders($filePath) {
|
||||
$entitiesArr = array();
|
||||
|
||||
$classes = ClassInfo::classes_for_file($filePath);
|
||||
if($classes) foreach($classes as $class) {
|
||||
if(class_exists($class) && method_exists($class, 'i18nCollectStatics')) {
|
||||
if(class_exists($class) && method_exists($class, 'provideI18nEntities')) {
|
||||
$obj = singleton($class);
|
||||
$entitiesArr = array_merge($entitiesArr,(array)$obj->i18nCollectStatics());
|
||||
$entitiesArr = array_merge($entitiesArr,(array)$obj->provideI18nEntities());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,8 +292,7 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
*
|
||||
* @return string User friendly translated singular name of this DataObject
|
||||
*/
|
||||
function i18n_singular_name()
|
||||
{
|
||||
function i18n_singular_name() {
|
||||
$name = (!empty($this->add_action)) ? $this->add_action : $this->singular_name();
|
||||
return _t($this->class.'.SINGULARNAME', $name);
|
||||
}
|
||||
@ -2819,6 +2818,60 @@ class DataObject extends ViewableData implements DataObjectInterface {
|
||||
*/
|
||||
public static $summary_fields = null;
|
||||
|
||||
/**
|
||||
* Collect all static properties on the object
|
||||
* which contain natural language, and need to be translated.
|
||||
* The full entity name is composed from the class name and a custom identifier.
|
||||
*
|
||||
* @return array A numerical array which contains one or more entities in array-form.
|
||||
* Each numeric entity array contains the "arguments" for a _t() call as array values:
|
||||
* $entity, $string, $priority, $context.
|
||||
*/
|
||||
public function provideI18nEntities() {
|
||||
$entities = array();
|
||||
|
||||
$db = $this->uninherited('db', true);
|
||||
if($db) foreach($db as $name => $type) {
|
||||
$entities["{$this->class}.db_{$name}"] = array(
|
||||
$name,
|
||||
PR_MEDIUM,
|
||||
'Name of the object property, mainly used for automatically generating forms'
|
||||
);
|
||||
}
|
||||
|
||||
$has_many = $this->uninherited('has_many', true);
|
||||
if($has_many) foreach($has_many as $name => $class) {
|
||||
$entities["{$this->class}.has_many_{$name}"] = array(
|
||||
$name,
|
||||
PR_MEDIUM,
|
||||
'Name of an object relation, mainly used for automatically generating forms'
|
||||
);
|
||||
}
|
||||
|
||||
$many_many = $this->uninherited('many_many', true);
|
||||
if($many_many) foreach($many_many as $name => $class) {
|
||||
$entities["{$this->class}.many_many_{$name}"] = array(
|
||||
$name,
|
||||
PR_MEDIUM,
|
||||
'Name of an object relation, mainly used for automatically generating forms'
|
||||
);
|
||||
}
|
||||
|
||||
$entities["{$this->class}.SINGULARNAME"] = array(
|
||||
$this->uninherited('singular_name', true),
|
||||
PR_MEDIUM,
|
||||
'Singular name of the object, used in dropdowns and to generally identify a single object in the interface'
|
||||
);
|
||||
|
||||
$entities["{$this->class}.PLURALNAME"] = array(
|
||||
$this->uninherited('plural_name', true),
|
||||
PR_MEDIUM,
|
||||
'Pural name of the object, used in dropdowns and to generally identify a collection of this object in the interface'
|
||||
);
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -1,7 +0,0 @@
|
||||
<% if Foo %>
|
||||
<% control Foo %><% end_control %>
|
||||
<% _t('INCLUDENONAMESPACE', 'Include Value'); %>
|
||||
<% _t('Test.INCLUDEWITHNAMESPACE', 'Include Value with namespace'); %>
|
||||
<% include i18nTextCollectorTest_NestedInclude %>
|
||||
<% end_if %>
|
||||
_t(in text)
|
@ -1,2 +0,0 @@
|
||||
<% _t('NESTEDINCLUDE','Nested Include Value') %>
|
||||
<% _t('Test.NESTEDINCLUDEWITHNAMESPACE','Nested Include Value with namespace') %>
|
20
tests/i18n/i18nTextCollectorTestMyObject.php
Normal file
20
tests/i18n/i18nTextCollectorTestMyObject.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage tests
|
||||
*/
|
||||
class i18nTextCollectorTestMyObject extends DataObject implements TestOnly {
|
||||
static $db = array(
|
||||
'FirstProperty' => 'Varchar',
|
||||
'SecondProperty' => 'Int'
|
||||
);
|
||||
|
||||
static $has_many = array(
|
||||
'Relation' => 'Group'
|
||||
);
|
||||
|
||||
static $singular_name = "My Object";
|
||||
|
||||
static $plural_name = "My Objects";
|
||||
}
|
||||
?>
|
20
tests/i18n/i18nTextCollectorTestMySubObject.php
Normal file
20
tests/i18n/i18nTextCollectorTestMySubObject.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @package sapphire
|
||||
* @subpackage tests
|
||||
*/
|
||||
class i18nTextCollectorTestMySubObject extends i18nTextCollectorTestMyObject implements TestOnly {
|
||||
static $db = array(
|
||||
'SubProperty' => 'Varchar',
|
||||
);
|
||||
|
||||
static $has_many = array(
|
||||
'SubRelation' => 'Group'
|
||||
);
|
||||
|
||||
static $singular_name = "My Sub Object";
|
||||
|
||||
static $plural_name = "My Sub Objects";
|
||||
|
||||
}
|
||||
?>
|
@ -10,4 +10,69 @@ class i18nTest extends SapphireTest {
|
||||
$this->assertTrue(isset($translations['de_DE']), 'Checking for de_DE translation');
|
||||
}
|
||||
|
||||
function testDataObjectFieldLabels() {
|
||||
global $lang;
|
||||
$oldLocale = i18n::get_locale();
|
||||
i18n::set_locale('de_DE');
|
||||
$obj = new i18nTest_Object();
|
||||
|
||||
$lang['en_US']['i18nTest_Object']['db_MyProperty'] = 'MyProperty';
|
||||
$lang['de_DE']['i18nTest_Object']['db_MyProperty'] = 'Mein Attribut';
|
||||
$this->assertEquals(
|
||||
$obj->fieldLabel('MyProperty'),
|
||||
'Mein Attribut'
|
||||
);
|
||||
|
||||
$lang['en_US']['i18nTest_Object']['has_one_HasOneRelation'] = 'HasOneRelation';
|
||||
$lang['de_DE']['i18nTest_Object']['has_one_HasOneRelation'] = 'Eins zu eins';
|
||||
$this->assertEquals(
|
||||
$obj->fieldLabel('HasOneRelation'),
|
||||
'Eins zu eins'
|
||||
);
|
||||
|
||||
$lang['en_US']['i18nTest_Object']['has_many_HasManyRelation'] = 'HasManyRelation';
|
||||
$lang['de_DE']['i18nTest_Object']['has_many_HasManyRelation'] = 'Viel zu eins';
|
||||
$this->assertEquals(
|
||||
$obj->fieldLabel('HasManyRelation'),
|
||||
'Viel zu eins'
|
||||
);
|
||||
|
||||
$lang['en_US']['i18nTest_Object']['many_many_ManyManyRelation'] = 'ManyManyRelation';
|
||||
$lang['de_DE']['i18nTest_Object']['many_many_ManyManyRelation'] = 'Viel zu viel';
|
||||
$this->assertEquals(
|
||||
$obj->fieldLabel('ManyManyRelation'),
|
||||
'Viel zu viel'
|
||||
);
|
||||
|
||||
$lang['en_US']['i18nTest_Object']['db_MyUntranslatedProperty'] = 'MyUntranslatedProperty';
|
||||
$this->assertEquals(
|
||||
$obj->fieldLabel('MyUntranslatedProperty'),
|
||||
'My Untranslated Property'
|
||||
);
|
||||
|
||||
i18n::set_locale($oldLocale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class i18nTest_Object extends DataObject implements TestOnly {
|
||||
|
||||
static $db = array(
|
||||
'MyProperty' => 'Varchar',
|
||||
'MyUntranslatedProperty' => 'Text'
|
||||
);
|
||||
|
||||
static $has_one = array(
|
||||
'HasOneRelation' => 'Member'
|
||||
);
|
||||
|
||||
static $has_many = array(
|
||||
'HasManyRelation' => 'Member'
|
||||
);
|
||||
|
||||
static $many_many = array(
|
||||
'ManyManyRelation' => 'Member'
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
|
@ -21,26 +21,26 @@ class i18nTextCollectorTest extends SapphireTest {
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->alternateBasePath = Director::baseFolder() . "/sapphire/tests/i18n/_fakewebroot/";
|
||||
$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'] = Director::baseFolder() . $this->alternateBasePath . 'i18ntestmodule/code/i18nTestModule.php';
|
||||
$_CLASS_MANIFEST['i18nTestModule_Addition'] = Director::baseFolder() . $this->alternateBasePath . 'i18ntestmodule/code/i18nTestModule.php';
|
||||
$_CLASS_MANIFEST['i18nTestModule'] = $this->alternateBasePath . '/i18ntestmodule/code/i18nTestModule.php';
|
||||
$_CLASS_MANIFEST['i18nTestModule_Addition'] = $this->alternateBasePath . '/i18ntestmodule/code/i18nTestModule.php';
|
||||
|
||||
global $_TEMPLATE_MANIFEST;
|
||||
$_TEMPLATE_MANIFEST['i18nTestModule.ss'] = array(
|
||||
'main' => Director::baseFolder() . $this->alternateBasePath . 'i18ntestmodule/templates/i18nTestModule.ss',
|
||||
'Layout' => Director::baseFolder() . $this->alternateBasePath . 'i18ntestmodule/templates/Layout/i18nTestModule.ss',
|
||||
'main' => $this->alternateBasePath . '/i18ntestmodule/templates/i18nTestModule.ss',
|
||||
'Layout' => $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss',
|
||||
);
|
||||
$_TEMPLATE_MANIFEST['i18nTestModuleInclude.ss'] = array(
|
||||
'Includes' => Director::baseFolder() . $this->alternateBasePath . 'i18ntestmodule/templates/Includes/i18nTestModuleInclude.ss',
|
||||
'Includes' => $this->alternateBasePath . '/i18ntestmodule/templates/Includes/i18nTestModuleInclude.ss',
|
||||
);
|
||||
$_TEMPLATE_MANIFEST['i18nTestModule.ss'] = array(
|
||||
'main' => Director::baseFolder() . $this->alternateBasePath . 'i18ntestmodule/templates/i18nTestModule.ss',
|
||||
'Layout' => Director::baseFolder() . $this->alternateBasePath . 'i18ntestmodule/templates/Layout/i18nTestModule.ss',
|
||||
'main' => $this->alternateBasePath . '/i18ntestmodule/templates/i18nTestModule.ss',
|
||||
'Layout' => $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss',
|
||||
);
|
||||
}
|
||||
|
||||
@ -289,7 +289,7 @@ PHP;
|
||||
function testCollectFromIncludedTemplates() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
$templateFilePath = $this->alternateBasePath . 'i18ntestmodule/templates/Layout/i18nTestModule.ss';
|
||||
$templateFilePath = $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss';
|
||||
$html = file_get_contents($templateFilePath);
|
||||
$this->assertEquals(
|
||||
$c->collectFromTemplate($html, 'mymodule', 'RandomNamespace'),
|
||||
@ -360,5 +360,55 @@ PHP;
|
||||
$compareContent
|
||||
);
|
||||
}
|
||||
|
||||
function testCollectFromEntityProvidersInCustomObject() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
$filePath = Director::baseFolder() . '/sapphire/tests/i18n/i18nTextCollectorTestMyObject.php';
|
||||
$matches = $c->collectFromEntityProviders($filePath);
|
||||
$this->assertEquals(
|
||||
array_keys($matches),
|
||||
array(
|
||||
'i18nTextCollectorTestMyObject.PLURALNAME',
|
||||
'i18nTextCollectorTestMyObject.SINGULARNAME',
|
||||
'i18nTextCollectorTestMyObject.db_FirstProperty',
|
||||
'i18nTextCollectorTestMyObject.db_SecondProperty',
|
||||
'i18nTextCollectorTestMyObject.has_many_Relation',
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'FirstProperty',
|
||||
$matches['i18nTextCollectorTestMyObject.db_FirstProperty'][0]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'My Object',
|
||||
$matches['i18nTextCollectorTestMyObject.SINGULARNAME'][0]
|
||||
);
|
||||
}
|
||||
|
||||
function testCollectFromEntityProvidersInCustomSubClass() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
$filePath = Director::baseFolder() . '/sapphire/tests/i18n/i18nTextCollectorTestMySubObject.php';
|
||||
$matches = $c->collectFromEntityProviders($filePath);
|
||||
$this->assertEquals(
|
||||
array_keys($matches),
|
||||
array(
|
||||
'i18nTextCollectorTestMySubObject.PLURALNAME',
|
||||
'i18nTextCollectorTestMySubObject.SINGULARNAME',
|
||||
'i18nTextCollectorTestMySubObject.db_SubProperty',
|
||||
'i18nTextCollectorTestMySubObject.has_many_SubRelation',
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'SubProperty',
|
||||
$matches['i18nTextCollectorTestMySubObject.db_SubProperty'][0]
|
||||
);
|
||||
$this->assertEquals(
|
||||
'My Sub Object',
|
||||
$matches['i18nTextCollectorTestMySubObject.SINGULARNAME'][0]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
Loading…
Reference in New Issue
Block a user