mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2024-11-15 03:18:26 +00:00
Add context support to the I18n class which resolves cakephp/cakephp#2063
This change adds Gettext context support to the I18n class. This allows custom translations for verbs and nouns and more.
This commit is contained in:
parent
7ea6626a15
commit
b47f91c47c
7 changed files with 124 additions and 19 deletions
|
@ -251,13 +251,17 @@ class ExtractTask extends AppShell {
|
|||
protected function _addTranslation($category, $domain, $msgid, $details = array()) {
|
||||
if (empty($this->_translations[$category][$domain][$msgid])) {
|
||||
$this->_translations[$category][$domain][$msgid] = array(
|
||||
'msgid_plural' => false
|
||||
'msgid_plural' => false,
|
||||
'msgctxt' => ''
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($details['msgid_plural'])) {
|
||||
$this->_translations[$category][$domain][$msgid]['msgid_plural'] = $details['msgid_plural'];
|
||||
}
|
||||
if (isset($details['msgctxt'])) {
|
||||
$this->_translations[$category][$domain][$msgid]['msgctxt'] = $details['msgctxt'];
|
||||
}
|
||||
|
||||
if (isset($details['file'])) {
|
||||
$line = 0;
|
||||
|
@ -374,6 +378,8 @@ class ExtractTask extends AppShell {
|
|||
$this->_parse('__dc', array('domain', 'singular', 'category'));
|
||||
$this->_parse('__dn', array('domain', 'singular', 'plural'));
|
||||
$this->_parse('__dcn', array('domain', 'singular', 'plural', 'count', 'category'));
|
||||
|
||||
$this->_parse('__x', array('context', 'singular'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,6 +433,9 @@ class ExtractTask extends AppShell {
|
|||
if (isset($plural)) {
|
||||
$details['msgid_plural'] = $plural;
|
||||
}
|
||||
if (isset($context)) {
|
||||
$details['msgctxt'] = $context;
|
||||
}
|
||||
$this->_addTranslation($categoryName, $domain, $singular, $details);
|
||||
} else {
|
||||
$this->_markerError($this->_file, $line, $functionName, $count);
|
||||
|
@ -551,6 +560,7 @@ class ExtractTask extends AppShell {
|
|||
foreach ($domains as $domain => $translations) {
|
||||
foreach ($translations as $msgid => $details) {
|
||||
$plural = $details['msgid_plural'];
|
||||
$context = $details['msgctxt'];
|
||||
$files = $details['references'];
|
||||
$occurrences = array();
|
||||
foreach ($files as $file => $lines) {
|
||||
|
@ -560,11 +570,15 @@ class ExtractTask extends AppShell {
|
|||
$occurrences = implode("\n#: ", $occurrences);
|
||||
$header = '#: ' . str_replace(DS, '/', str_replace($paths, '', $occurrences)) . "\n";
|
||||
|
||||
$sentence = '';
|
||||
if ($context) {
|
||||
$sentence .= "msgctxt \"{$context}\"\n";
|
||||
}
|
||||
if ($plural === false) {
|
||||
$sentence = "msgid \"{$msgid}\"\n";
|
||||
$sentence .= "msgid \"{$msgid}\"\n";
|
||||
$sentence .= "msgstr \"\"\n\n";
|
||||
} else {
|
||||
$sentence = "msgid \"{$msgid}\"\n";
|
||||
$sentence .= "msgid \"{$msgid}\"\n";
|
||||
$sentence .= "msgid_plural \"{$plural}\"\n";
|
||||
$sentence .= "msgstr[0] \"\"\n";
|
||||
$sentence .= "msgstr[1] \"\"\n\n";
|
||||
|
|
|
@ -188,10 +188,13 @@ class I18n {
|
|||
* @param integer $count Count Count is used with $plural to choose the correct plural form.
|
||||
* @param string $language Language to translate string to.
|
||||
* If null it checks for language in session followed by Config.language configuration variable.
|
||||
* @param string $context Context The context of the translation, e.g a verb or a noun.
|
||||
* @return string translated string.
|
||||
* @throws CakeException When '' is provided as a domain.
|
||||
*/
|
||||
public static function translate($singular, $plural = null, $domain = null, $category = self::LC_MESSAGES, $count = null, $language = null) {
|
||||
public static function translate($singular, $plural = null, $domain = null, $category = self::LC_MESSAGES,
|
||||
$count = null, $language = null, $context = null
|
||||
) {
|
||||
$_this = I18n::getInstance();
|
||||
|
||||
if (strpos($singular, "\r\n") !== false) {
|
||||
|
@ -254,8 +257,10 @@ class I18n {
|
|||
}
|
||||
}
|
||||
|
||||
if (!empty($_this->_domains[$domain][$_this->_lang][$_this->category][$singular])) {
|
||||
if (($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$singular]) || ($plurals) && ($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$plural])) {
|
||||
if (!empty($_this->_domains[$domain][$_this->_lang][$_this->category][$singular][$context])) {
|
||||
if (($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$singular][$context]) ||
|
||||
($plurals) && ($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$plural][$context])
|
||||
) {
|
||||
if (is_array($trans)) {
|
||||
if (isset($trans[$plurals])) {
|
||||
$trans = $trans[$plurals];
|
||||
|
@ -469,6 +474,7 @@ class I18n {
|
|||
// Binary files extracted makes non-standard local variables
|
||||
if ($data = file_get_contents($filename)) {
|
||||
$translations = array();
|
||||
$context = null;
|
||||
$header = substr($data, 0, 20);
|
||||
$header = unpack('L1magic/L1version/L1count/L1o_msg/L1o_trn', $header);
|
||||
extract($header);
|
||||
|
@ -488,6 +494,10 @@ class I18n {
|
|||
if (strpos($msgstr, "\000")) {
|
||||
$msgstr = explode("\000", $msgstr);
|
||||
}
|
||||
|
||||
if ($msgid != '') {
|
||||
$msgstr = array($context => $msgstr);
|
||||
}
|
||||
$translations[$msgid] = $msgstr;
|
||||
|
||||
if (isset($msgid_plural)) {
|
||||
|
@ -515,12 +525,15 @@ class I18n {
|
|||
$type = 0;
|
||||
$translations = array();
|
||||
$translationKey = '';
|
||||
$translationContext = null;
|
||||
$plural = 0;
|
||||
$header = '';
|
||||
|
||||
do {
|
||||
$line = trim(fgets($file));
|
||||
if ($line === '' || $line[0] === '#') {
|
||||
$translationContext = null;
|
||||
|
||||
continue;
|
||||
}
|
||||
if (preg_match("/msgid[[:space:]]+\"(.+)\"$/i", $line, $regs)) {
|
||||
|
@ -529,31 +542,33 @@ class I18n {
|
|||
} elseif (preg_match("/msgid[[:space:]]+\"\"$/i", $line, $regs)) {
|
||||
$type = 2;
|
||||
$translationKey = '';
|
||||
} elseif (preg_match("/msgctxt[[:space:]]+\"(.+)\"$/i", $line, $regs)) {
|
||||
$translationContext = $regs[1];
|
||||
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && ($type == 1 || $type == 2 || $type == 3)) {
|
||||
$type = 3;
|
||||
$translationKey .= stripcslashes($regs[1]);
|
||||
} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
|
||||
$translations[$translationKey] = stripcslashes($regs[1]);
|
||||
$translations[$translationKey][$translationContext] = stripcslashes($regs[1]);
|
||||
$type = 4;
|
||||
} elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
|
||||
$type = 4;
|
||||
$translations[$translationKey] = '';
|
||||
$translations[$translationKey][$translationContext] = '';
|
||||
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 4 && $translationKey) {
|
||||
$translations[$translationKey] .= stripcslashes($regs[1]);
|
||||
$translations[$translationKey][$translationContext] .= stripcslashes($regs[1]);
|
||||
} elseif (preg_match("/msgid_plural[[:space:]]+\".*\"$/i", $line, $regs)) {
|
||||
$type = 6;
|
||||
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 6 && $translationKey) {
|
||||
$type = 6;
|
||||
} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
|
||||
$plural = $regs[1];
|
||||
$translations[$translationKey][$plural] = stripcslashes($regs[2]);
|
||||
$translations[$translationKey][$translationContext][$plural] = stripcslashes($regs[2]);
|
||||
$type = 7;
|
||||
} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
|
||||
$plural = $regs[1];
|
||||
$translations[$translationKey][$plural] = '';
|
||||
$translations[$translationKey][$translationContext][$plural] = '';
|
||||
$type = 7;
|
||||
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 7 && $translationKey) {
|
||||
$translations[$translationKey][$plural] .= stripcslashes($regs[1]);
|
||||
$translations[$translationKey][$translationContext][$plural] .= stripcslashes($regs[1]);
|
||||
} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && $type == 2 && !$translationKey) {
|
||||
$header .= stripcslashes($regs[1]);
|
||||
$type = 5;
|
||||
|
@ -563,9 +578,10 @@ class I18n {
|
|||
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 5) {
|
||||
$header .= stripcslashes($regs[1]);
|
||||
} else {
|
||||
unset($translations[$translationKey]);
|
||||
unset($translations[$translationKey][$translationContext]);
|
||||
$type = 0;
|
||||
$translationKey = '';
|
||||
$translationContext = null;
|
||||
$plural = 0;
|
||||
}
|
||||
} while (!feof($file));
|
||||
|
|
|
@ -162,6 +162,10 @@ class ExtractTaskTest extends CakeTestCase {
|
|||
$this->assertContains('msgid "double \\"quoted\\""', $result, 'Strings with quotes not handled correctly');
|
||||
$this->assertContains("msgid \"single 'quoted'\"", $result, 'Strings with quotes not handled correctly');
|
||||
|
||||
$pattern = '/\#: (\\\\|\/)extract\.ctp:33\n';
|
||||
$pattern .= 'msgctxt "mail"/';
|
||||
$this->assertRegExp($pattern, $result);
|
||||
|
||||
// extract.ctp - reading the domain.pot
|
||||
$result = file_get_contents($this->path . DS . 'domain.pot');
|
||||
|
||||
|
|
|
@ -75,15 +75,15 @@ class I18nTest extends CakeTestCase {
|
|||
$this->assertEquals('Dom 1 Foo', I18n::translate('dom1.foo', false, 'dom1'));
|
||||
$this->assertEquals('Dom 1 Bar', I18n::translate('dom1.bar', false, 'dom1'));
|
||||
$domains = I18n::domains();
|
||||
$this->assertEquals('Dom 1 Foo', $domains['dom1']['cache_test_po']['LC_MESSAGES']['dom1.foo']);
|
||||
$this->assertEquals('Dom 1 Foo', $domains['dom1']['cache_test_po']['LC_MESSAGES']['dom1.foo']['']);
|
||||
|
||||
// reset internally stored entries
|
||||
I18n::clear();
|
||||
|
||||
// now only dom1 should be in cache
|
||||
$cachedDom1 = Cache::read('dom1_' . $lang, '_cake_core_');
|
||||
$this->assertEquals('Dom 1 Foo', $cachedDom1['LC_MESSAGES']['dom1.foo']);
|
||||
$this->assertEquals('Dom 1 Bar', $cachedDom1['LC_MESSAGES']['dom1.bar']);
|
||||
$this->assertEquals('Dom 1 Foo', $cachedDom1['LC_MESSAGES']['dom1.foo']['']);
|
||||
$this->assertEquals('Dom 1 Bar', $cachedDom1['LC_MESSAGES']['dom1.bar']['']);
|
||||
// dom2 not in cache
|
||||
$this->assertFalse(Cache::read('dom2_' . $lang, '_cake_core_'));
|
||||
|
||||
|
@ -92,11 +92,11 @@ class I18nTest extends CakeTestCase {
|
|||
|
||||
// verify dom2 was cached through manual read from cache
|
||||
$cachedDom2 = Cache::read('dom2_' . $lang, '_cake_core_');
|
||||
$this->assertEquals('Dom 2 Foo', $cachedDom2['LC_MESSAGES']['dom2.foo']);
|
||||
$this->assertEquals('Dom 2 Bar', $cachedDom2['LC_MESSAGES']['dom2.bar']);
|
||||
$this->assertEquals('Dom 2 Foo', $cachedDom2['LC_MESSAGES']['dom2.foo']['']);
|
||||
$this->assertEquals('Dom 2 Bar', $cachedDom2['LC_MESSAGES']['dom2.bar']['']);
|
||||
|
||||
// modify cache entry manually to verify that dom1 entries now will be read from cache
|
||||
$cachedDom1['LC_MESSAGES']['dom1.foo'] = 'FOO';
|
||||
$cachedDom1['LC_MESSAGES']['dom1.foo'][''] = 'FOO';
|
||||
Cache::write('dom1_' . $lang, $cachedDom1, '_cake_core_');
|
||||
$this->assertEquals('FOO', I18n::translate('dom1.foo', false, 'dom1'));
|
||||
}
|
||||
|
@ -1879,6 +1879,22 @@ class I18nTest extends CakeTestCase {
|
|||
$this->assertSame($expected, $result['day']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic context support
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testContext() {
|
||||
Configure::write('Config.language', 'nld');
|
||||
|
||||
$this->assertSame("brief", __x('mail', 'letter'));
|
||||
$this->assertSame("letter", __x('character', 'letter'));
|
||||
$this->assertSame("bal", __x('spherical object', 'ball'));
|
||||
$this->assertSame("danspartij", __x('social gathering', 'ball'));
|
||||
$this->assertSame("balans", __('balance'));
|
||||
$this->assertSame("saldo", __x('money', 'balance'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Singular method
|
||||
*
|
||||
|
|
29
lib/Cake/Test/test_app/Locale/nld/LC_MESSAGES/default.po
Normal file
29
lib/Cake/Test/test_app/Locale/nld/LC_MESSAGES/default.po
Normal file
|
@ -0,0 +1,29 @@
|
|||
msgctxt "character"
|
||||
msgid "letter"
|
||||
msgid_plural "letters"
|
||||
msgstr[0] "letter"
|
||||
msgstr[1] "letters"
|
||||
|
||||
msgctxt "mail"
|
||||
msgid "letter"
|
||||
msgid_plural "letters"
|
||||
msgstr[0] "brief"
|
||||
msgstr[1] "brieven"
|
||||
|
||||
msgctxt "spherical object"
|
||||
msgid "ball"
|
||||
msgstr "bal"
|
||||
|
||||
msgctxt "social gathering"
|
||||
msgid "ball"
|
||||
msgstr "danspartij"
|
||||
|
||||
msgid "ball"
|
||||
msgstr "bal"
|
||||
|
||||
msgid "balance"
|
||||
msgstr "balans"
|
||||
|
||||
msgctxt "money"
|
||||
msgid "balance"
|
||||
msgstr "saldo"
|
|
@ -29,3 +29,5 @@ __('Hot features!'
|
|||
|
||||
// Category
|
||||
echo __c('You have a new message (category: LC_TIME).', 5);
|
||||
|
||||
echo __x('mail', 'letter');
|
||||
|
|
|
@ -748,6 +748,30 @@ if (!function_exists('__c')) {
|
|||
|
||||
}
|
||||
|
||||
if (!function_exists('__x')) {
|
||||
|
||||
/**
|
||||
* Returns a translated string if one is found; Otherwise, the submitted message.
|
||||
*
|
||||
* @param string $context Context of the text
|
||||
* @param string $singular Text to translate
|
||||
* @param mixed $args Array with arguments or multiple arguments in function
|
||||
* @return mixed translated string
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__
|
||||
*/
|
||||
function __x($context, $singular, $args = null) {
|
||||
if (!$singular) {
|
||||
return;
|
||||
}
|
||||
|
||||
App::uses('I18n', 'I18n');
|
||||
$translated = I18n::translate($singular, null, null, null, null, null, $context);
|
||||
$arguments = func_get_args();
|
||||
return I18n::insertArgs($translated, array_slice($arguments, 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!function_exists('LogError')) {
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue