From 88d7fbeb53d8d59466813ae3769db8cc5025aec0 Mon Sep 17 00:00:00 2001 From: Mark van Driel Date: Sun, 25 Aug 2013 23:04:05 +0200 Subject: [PATCH] Extract category strings to separate .po files --- lib/Cake/Console/Command/Task/ExtractTask.php | 172 ++++++++++-------- .../Console/Command/Task/ExtractTaskTest.php | 26 +++ lib/Cake/Test/test_app/View/Pages/extract.ctp | 3 + 3 files changed, 127 insertions(+), 74 deletions(-) diff --git a/lib/Cake/Console/Command/Task/ExtractTask.php b/lib/Cake/Console/Command/Task/ExtractTask.php index 88b54b916..981b94dfe 100644 --- a/lib/Cake/Console/Command/Task/ExtractTask.php +++ b/lib/Cake/Console/Command/Task/ExtractTask.php @@ -44,7 +44,7 @@ class ExtractTask extends AppShell { protected $_files = array(); /** - * Merge all domains string into the default.pot file + * Merge all domains and categories string into the default.pot file * * @var boolean */ @@ -72,7 +72,7 @@ class ExtractTask extends AppShell { protected $_tokens = array(); /** - * Extracted strings indexed by domain. + * Extracted strings indexed by category and domain. * * @var array */ @@ -222,7 +222,7 @@ class ExtractTask extends AppShell { $this->_merge = !(strtolower($this->params['merge']) === 'no'); } else { $this->out(); - $response = $this->in(__d('cake_console', 'Would you like to merge all domains strings into the default.pot file?'), array('y', 'n'), 'n'); + $response = $this->in(__d('cake_console', 'Would you like to merge all domains and categories strings into the default.pot file?'), array('y', 'n'), 'n'); $this->_merge = strtolower($response) === 'y'; } @@ -244,20 +244,21 @@ class ExtractTask extends AppShell { * * Takes care of duplicate translations * + * @param string $category * @param string $domain * @param string $msgid * @param array $details * @return void */ - protected function _addTranslation($domain, $msgid, $details = array()) { - if (empty($this->_translations[$domain][$msgid])) { - $this->_translations[$domain][$msgid] = array( + protected function _addTranslation($category, $domain, $msgid, $details = array()) { + if (empty($this->_translations[$category][$domain][$msgid])) { + $this->_translations[$category][$domain][$msgid] = array( 'msgid_plural' => false ); } if (isset($details['msgid_plural'])) { - $this->_translations[$domain][$msgid]['msgid_plural'] = $details['msgid_plural']; + $this->_translations[$category][$domain][$msgid]['msgid_plural'] = $details['msgid_plural']; } if (isset($details['file'])) { @@ -265,7 +266,7 @@ class ExtractTask extends AppShell { if (isset($details['line'])) { $line = $details['line']; } - $this->_translations[$domain][$msgid]['references'][$details['file']][] = $line; + $this->_translations[$category][$domain][$msgid]['references'][$details['file']][] = $line; } } @@ -307,7 +308,7 @@ class ExtractTask extends AppShell { ->addOption('app', array('help' => __d('cake_console', 'Directory where your application is located.'))) ->addOption('paths', array('help' => __d('cake_console', 'Comma separated list of paths.'))) ->addOption('merge', array( - 'help' => __d('cake_console', 'Merge all domain strings into the default.po file.'), + 'help' => __d('cake_console', 'Merge all domain and category strings into the default.po file.'), 'choices' => array('yes', 'no') )) ->addOption('output', array('help' => __d('cake_console', 'Full path to output directory.'))) @@ -368,10 +369,10 @@ class ExtractTask extends AppShell { $this->_parse('__', array('singular')); $this->_parse('__n', array('singular', 'plural')); $this->_parse('__d', array('domain', 'singular')); - $this->_parse('__c', array('singular')); - $this->_parse('__dc', array('domain', 'singular')); + $this->_parse('__c', array('singular', 'category')); + $this->_parse('__dc', array('domain', 'singular', 'category')); $this->_parse('__dn', array('domain', 'singular', 'plural')); - $this->_parse('__dcn', array('domain', 'singular', 'plural')); + $this->_parse('__dcn', array('domain', 'singular', 'plural', 'count', 'category')); } } @@ -379,11 +380,12 @@ class ExtractTask extends AppShell { * Parse tokens * * @param string $functionName Function name that indicates translatable string (e.g: '__') - * @param array $map Array containing what variables it will find (e.g: domain, singular, plural) + * @param array $map Array containing what variables it will find (e.g: category, domain, singular, plural) * @return void */ protected function _parse($functionName, $map) { $count = 0; + $allCategoryNames = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MONETARY', 'LC_MESSAGES', 'LC_ALL', 'LC_MESSAGES'); $tokenCount = count($this->_tokens); while (($tokenCount - $count) > 1) { @@ -413,6 +415,14 @@ class ExtractTask extends AppShell { if ($mapCount == count($strings)) { extract(array_combine($map, $strings)); + $category = isset($category) ? $category : LC_MESSAGES; + $category = intval($category); + foreach ($allCategoryNames as $name) { + if ($category === constant($name)) { + $categoryName = $name; + break; + } + } $domain = isset($domain) ? $domain : 'default'; $details = array( 'file' => $this->_file, @@ -421,7 +431,7 @@ class ExtractTask extends AppShell { if (isset($plural)) { $details['msgid_plural'] = $plural; } - $this->_addTranslation($domain, $singular, $details); + $this->_addTranslation($categoryName, $domain, $singular, $details); } else { $this->_markerError($this->_file, $line, $functionName, $count); } @@ -500,7 +510,7 @@ class ExtractTask extends AppShell { * @param string $domain default domain to bind the validations to * @return void */ - protected function _processValidationRules($field, $rules, $file, $domain) { + protected function _processValidationRules($field, $rules, $file, $domain, $category = 'LC_MESSAGES') { if (!is_array($rules)) { return; } @@ -526,7 +536,7 @@ class ExtractTask extends AppShell { 'file' => $file, 'line' => 'validation for field ' . $field ); - $this->_addTranslation($domain, $msgid, $details); + $this->_addTranslation($category, $domain, $msgid, $details); } } } @@ -539,31 +549,33 @@ class ExtractTask extends AppShell { protected function _buildFiles() { $paths = $this->_paths; $paths[] = realpath(APP) . DS; - foreach ($this->_translations as $domain => $translations) { - foreach ($translations as $msgid => $details) { - $plural = $details['msgid_plural']; - $files = $details['references']; - $occurrences = array(); - foreach ($files as $file => $lines) { - $lines = array_unique($lines); - $occurrences[] = $file . ':' . implode(';', $lines); - } - $occurrences = implode("\n#: ", $occurrences); - $header = '#: ' . str_replace(DS, '/', str_replace($paths, '', $occurrences)) . "\n"; + foreach ($this->_translations as $category => $domains) { + foreach ($domains as $domain => $translations) { + foreach ($translations as $msgid => $details) { + $plural = $details['msgid_plural']; + $files = $details['references']; + $occurrences = array(); + foreach ($files as $file => $lines) { + $lines = array_unique($lines); + $occurrences[] = $file . ':' . implode(';', $lines); + } + $occurrences = implode("\n#: ", $occurrences); + $header = '#: ' . str_replace(DS, '/', str_replace($paths, '', $occurrences)) . "\n"; - if ($plural === false) { - $sentence = "msgid \"{$msgid}\"\n"; - $sentence .= "msgstr \"\"\n\n"; - } else { - $sentence = "msgid \"{$msgid}\"\n"; - $sentence .= "msgid_plural \"{$plural}\"\n"; - $sentence .= "msgstr[0] \"\"\n"; - $sentence .= "msgstr[1] \"\"\n\n"; - } + if ($plural === false) { + $sentence = "msgid \"{$msgid}\"\n"; + $sentence .= "msgstr \"\"\n\n"; + } else { + $sentence = "msgid \"{$msgid}\"\n"; + $sentence .= "msgid_plural \"{$plural}\"\n"; + $sentence .= "msgstr[0] \"\"\n"; + $sentence .= "msgstr[1] \"\"\n\n"; + } - $this->_store($domain, $header, $sentence); - if ($domain !== 'default' && $this->_merge) { - $this->_store('default', $header, $sentence); + $this->_store($category, $domain, $header, $sentence); + if (($category !== 'LC_MESSAGES' || $domain !== 'default') && $this->_merge) { + $this->_store('LC_MESSAGES', 'default', $header, $sentence); + } } } } @@ -572,19 +584,23 @@ class ExtractTask extends AppShell { /** * Prepare a file to be stored * + * @param string $category * @param string $domain * @param string $header * @param string $sentence * @return void */ - protected function _store($domain, $header, $sentence) { - if (!isset($this->_storage[$domain])) { - $this->_storage[$domain] = array(); + protected function _store($category, $domain, $header, $sentence) { + if (!isset($this->_storage[$category])) { + $this->_storage[$category] = array(); } - if (!isset($this->_storage[$domain][$sentence])) { - $this->_storage[$domain][$sentence] = $header; + if (!isset($this->_storage[$category][$domain])) { + $this->_storage[$category][$domain] = array(); + } + if (!isset($this->_storage[$category][$domain][$sentence])) { + $this->_storage[$category][$domain][$sentence] = $header; } else { - $this->_storage[$domain][$sentence] .= $header; + $this->_storage[$category][$domain][$sentence] .= $header; } } @@ -598,36 +614,42 @@ class ExtractTask extends AppShell { if (!empty($this->params['overwrite'])) { $overwriteAll = true; } - - foreach ($this->_storage as $domain => $sentences) { - $output = $this->_writeHeader(); - foreach ($sentences as $sentence => $header) { - $output .= $header . $sentence; - } - - $filename = $domain . '.pot'; - $File = new File($this->_output . $filename); - $response = ''; - while ($overwriteAll === false && $File->exists() && strtoupper($response) !== 'Y') { - $this->out(); - $response = $this->in( - __d('cake_console', 'Error: %s already exists in this location. Overwrite? [Y]es, [N]o, [A]ll', $filename), - array('y', 'n', 'a'), - 'y' - ); - if (strtoupper($response) === 'N') { - $response = ''; - while (!$response) { - $response = $this->in(__d('cake_console', "What would you like to name this file?"), null, 'new_' . $filename); - $File = new File($this->_output . $response); - $filename = $response; - } - } elseif (strtoupper($response) === 'A') { - $overwriteAll = true; + foreach ($this->_storage as $category => $domains) { + foreach ($domains as $domain => $sentences) { + $output = $this->_writeHeader(); + foreach ($sentences as $sentence => $header) { + $output .= $header . $sentence; } + + $filename = $domain . '.pot'; + if ($category === 'LC_MESSAGES') { + $File = new File($this->_output . $filename); + } else { + $Folder = new Folder($this->_output . $category, true); + $File = new File($this->_output . $category . DS . $filename); + } + $response = ''; + while ($overwriteAll === false && $File->exists() && strtoupper($response) !== 'Y') { + $this->out(); + $response = $this->in( + __d('cake_console', 'Error: %s already exists in this location. Overwrite? [Y]es, [N]o, [A]ll', $filename), + array('y', 'n', 'a'), + 'y' + ); + if (strtoupper($response) === 'N') { + $response = ''; + while (!$response) { + $response = $this->in(__d('cake_console', "What would you like to name this file?"), null, 'new_' . $filename); + $File = new File($this->_output . $response); + $filename = $response; + } + } elseif (strtoupper($response) === 'A') { + $overwriteAll = true; + } + } + $File->write($output); + $File->close(); } - $File->write($output); - $File->close(); } } @@ -665,7 +687,7 @@ class ExtractTask extends AppShell { protected function _getStrings(&$position, $target) { $strings = array(); $count = count($strings); - while ($count < $target && ($this->_tokens[$position] === ',' || $this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING)) { + while ($count < $target && ($this->_tokens[$position] === ',' || $this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING || $this->_tokens[$position][0] == T_LNUMBER)) { $count = count($strings); if ($this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING && $this->_tokens[$position + 1] === '.') { $string = ''; @@ -678,6 +700,8 @@ class ExtractTask extends AppShell { $strings[] = $string; } elseif ($this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING) { $strings[] = $this->_formatString($this->_tokens[$position][1]); + } elseif ($this->_tokens[$position][0] == T_LNUMBER) { + $strings[] = $this->_tokens[$position][1]; } $position++; } diff --git a/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php index 485268928..a38e51ade 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php @@ -178,6 +178,32 @@ class ExtractTaskTest extends CakeTestCase { $this->assertRegExp($pattern, $result); } +/** + * testExtractCategory method + * + * @return void + */ + public function testExtractCategory() { + $this->Task->interactive = false; + + $this->Task->params['paths'] = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Pages'; + $this->Task->params['output'] = $this->path . DS; + $this->Task->params['extract-core'] = 'no'; + $this->Task->params['merge'] = 'no'; + $this->Task->expects($this->never())->method('err'); + $this->Task->expects($this->any())->method('in') + ->will($this->returnValue('y')); + $this->Task->expects($this->never())->method('_stop'); + + $this->Task->execute(); + $this->assertTrue(file_exists($this->path . DS . 'LC_TIME' . DS . 'default.pot')); + + $result = file_get_contents($this->path . DS . 'default.pot'); + + $pattern = '/\#: .*extract\.ctp:31\n/'; + $this->assertNotRegExp($pattern, $result); + } + /** * test exclusions * diff --git a/lib/Cake/Test/test_app/View/Pages/extract.ctp b/lib/Cake/Test/test_app/View/Pages/extract.ctp index 3d89ffc9f..bcfc9c884 100644 --- a/lib/Cake/Test/test_app/View/Pages/extract.ctp +++ b/lib/Cake/Test/test_app/View/Pages/extract.ctp @@ -26,3 +26,6 @@ __('Hot features!' . ' Just look at the name...It\'s Cake' . "\n - Active, Friendly Community:" . ' Join us #cakephp on IRC. We\'d love to help you get started'); + +// Category +echo __c('You have a new message (category: LC_TIME).', 2);