ENHANCEMENT Backwards compatible $priority parameter for i18n::_t(): Allow numeric arrays as parameters, as well as %s style replacements with a named parameter map (easier transition to new system with existing translation strings)

This commit is contained in:
Ingo Schommer 2012-05-01 20:56:07 +02:00
parent 2288d80c30
commit e949b6f2b0
4 changed files with 83 additions and 9 deletions

View File

@ -455,13 +455,36 @@ and was rarely used in practice - so we moved it to a "[homepagefordomain](https
### New syntax for translatable _t functions [i18n-t]###
You can now call the _t() function in both templates and code with a namespace and string to translate, as well as a
comment and injection array. Note that the proxity arguement to _t is no longer supported.
You can now call the `_t()` function in both templates and code with a namespace and string to translate, as well as a comment and injection array.
The new syntax supports injecting variables into the translation. For example:
:::php
_t('i18nTestModule.INJECTIONS2', "Hello {name} {greeting}", array("name"=>"Paul", "greeting"=>"good you are here"));
_t(
'i18nTestModule.INJECTIONS2',
"Hello {name} {greeting}",
array("name"=>"Paul", "greeting"=>"good you are here")
);
We've written the injection logic in a way that keeps backwards compatible with
existing translations. This means that you can migrate from `sprintf()` to the new injection
API incrementally. The following to "mixed usage" examples still work, although they
don't get the advantage of flexible ordering in substitutions.
:::php
_t(
'i18nTestModule.INJECTIONS2',
"Hello {name} {greeting}",
array("Paul", "good you are here")
);
_t(
'i18nTestModule.INJECTIONS2',
"Hello %s, %s",
array("name"=>"Paul", "greeting"=>"good you are here")
);
Of course, you can keep using `sprintf()` for variable substitution in your own code.
### Default translation source in YML instead of PHP $lang array, using Zend_Translate {#zend-translate}

View File

@ -180,9 +180,10 @@ Therefore, the following would be a valid use in templates:
Using SS templating variables in the translatable string (e.g. $Author, $Date..) is not currently supported.
### Injection-support
### Injection Support
Variable injection in _t allows us to dynamically replace parts of a translated string, e.g. by a username or a page-title.
Variable injection in `_t()` allows us to dynamically replace parts of a translated string, e.g. by a username or a page-title. The named parameters also allow flexible ordering of placeholders,
which might vary depending on the used language.
:::php
// in PHP-file
@ -196,6 +197,10 @@ Variable injection in _t allows us to dynamically replace parts of a translated
// in SS-template ($Name must be available in the current template-scope)
<%t MYPROJECT.INJECTIONS "Hello {name} {greeting}" name="$Name" greeting="good to see you" %>
Note that you can still use `sprintf()` wrapped around a `_t()` call
for your substitutions. In contrast to `sprintf()`, our API has a more translator friendly
placeholder syntax, as well as more graceful fallback if not all placeholders are found
(an outdated translation with less placeholders will throw a notice rather than a fatal error).
## Collecting text

View File

@ -1512,9 +1512,36 @@ class i18n extends Object implements TemplateGlobalProvider {
}
// inject the variables from injectionArray (if present)
if ($injectionArray && count($injectionArray) > 0) {
foreach($injectionArray as $variable => $injection) {
$returnValue = str_replace('{'.$variable.'}', $injection, $returnValue);
if($injectionArray) {
$regex = '/\{[\w\d]*\}/i';
if(!preg_match($regex, $returnValue)) {
// Legacy mode: If no injection placeholders are found,
// replace sprintf placeholders in fixed order.
$returnValue = vsprintf($returnValue, array_values($injectionArray));
} else if(!ArrayLib::is_associative($injectionArray)) {
// Legacy mode: If injection placeholders are found,
// but parameters are passed without names, replace them in fixed order.
$returnValue = preg_replace_callback(
$regex,
function($matches) use(&$injectionArray) {
return $injectionArray ? array_shift($injectionArray) : '';
},
$returnValue
);
} else {
// Standard placeholder replacement with named injections and variable order.
foreach($injectionArray as $variable => $injection) {
$placeholder = '{'.$variable.'}';
$returnValue = str_replace($placeholder, $injection, $returnValue, $count);
if(!$count) {
SS_Log::log(sprintf(
"Couldn't find placeholder '%s' in translation string '%s' (id: '%s')",
$placeholder,
$returnValue,
$entity
), SS_Log::NOTICE);
}
}
}
}

View File

@ -251,7 +251,8 @@ class i18nTest extends SapphireTest {
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.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";
@ -287,6 +288,24 @@ class i18nTest extends SapphireTest {
$translated, "Testing a translation with just entity and injection array"
);
$translated = i18n::_t(
'i18nTestModule.INJECTIONSLEGACY', // has %s placeholders
array("name"=>"Cat", "greeting2"=>"meow", "goodbye"=>"meow")
);
$this->assertContains(
"TRANS Hello Cat meow. But it is late, meow",
$translated, "Testing sprintf placeholders with named injections"
);
$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"
);
i18n::set_locale($oldLocale);
}