From 90ae0ed18d279d7c41161ed4d42c6116824e218a Mon Sep 17 00:00:00 2001 From: Julian Seidenberg Date: Wed, 18 Apr 2012 17:23:57 +1200 Subject: [PATCH] BUGFIX: reverting back to Ingo's text collector from code, but using the parser to get from templates. Adding special case for _t functions in code that have an array in them. Fixing unit tests for all this. --- i18n/i18nTextCollector.php | 174 +++++++++------------------ tests/i18n/i18nTextCollectorTest.php | 13 +- 2 files changed, 65 insertions(+), 122 deletions(-) diff --git a/i18n/i18nTextCollector.php b/i18n/i18nTextCollector.php index 612719011..98f5eef9d 100644 --- a/i18n/i18nTextCollector.php +++ b/i18n/i18nTextCollector.php @@ -193,136 +193,80 @@ class i18nTextCollector extends Object { return $entities; } - + public function collectFromCode($content, $module) { - $entitiesArr = array(); + $entities = array(); - $newRegexRule = '/_t\s*\(\s*(.+?)\s*\)\s*;\s*/is'; + $tokens = token_get_all(" 0) { //we have at least one match + if($inTransFn && $id == T_ARRAY) { + //raw 'array' token found in _t function, stop processing the tokens for this _t now + $finalTokenDueToArray = true; + } - //take all the matched _t entities - foreach($matchesArray[1] as $match) { - //replace all commas with backticks (unique character to explode on later) - $replacedMatch = preg_replace('/("|\'|_LOW|_MEDIUM|_HIGH)\s*,\s*([\'"]|"|\'|array|PR)/','$1`$2',$match); //keep array text - - //$replacedMatch = trim($replacedMatch," \"'\n"); //remove starting and ending quotes - $replacedMatch = trim($replacedMatch," \n"); //remove starting and ending spaces and newlines - - $parts = explode('`',$replacedMatch); //cut up the _t call - - $partsWOQuotes = array(); - foreach($parts as $part) { - $part = trim($part,"\n"); //remove spaces and newlines from part - - $firstChar = substr($part,0,1); - if ($firstChar == "'" || $firstChar == '"') { - //remove wrapping quotes - $part = substr($part,1,-1); - - //remove inner concatenation - $part = preg_replace("/$firstChar\\s*\\.\\s*$firstChar/",'',$part); + if($id == T_STRING && $text == '_t') { + // start definition + $inTransFn = true; + } elseif($inTransFn && $id == T_VARIABLE) { + // Dynamic definition from provideEntities - skip + $inTransFn = false; + $inConcat = false; + $currentEntity = array(); + } elseif($inTransFn && $id == T_CONSTANT_ENCAPSED_STRING) { + // Fixed quoting escapes, and remove leading/trailing quotes + if(preg_match('/^\'/', $text)) { + $text = str_replace("\'", "'", $text); + $text = preg_replace('/^\'/', '', $text); + $text = preg_replace('/\'$/', '', $text); + } else { + $text = str_replace('\"', '"', $text); + $text = preg_replace('/^"/', '', $text); + $text = preg_replace('/"$/', '', $text); } - $partsWOQuotes[] = $part; //remove starting and ending quotes from inner parts - } - - if ($parts && count($partsWOQuotes) > 0) { - - $entitiesArr = array_merge($entitiesArr, (array)$this->entitySpecFromNewParts($partsWOQuotes)); + if($inConcat) { + $currentEntity[count($currentEntity)-1] .= $text; + } else { + $currentEntity[] = $text; + } } + } elseif($inTransFn && $token == '.') { + $inConcat = true; + } elseif($inTransFn && $token == ',') { + $inConcat = false; + } elseif($inTransFn && ($token == ')' || $finalTokenDueToArray)) { + // finalize definition + $inTransFn = false; + $inConcat = false; + $entity = array_shift($currentEntity); + $entities[$entity] = $currentEntity; + $currentEntity = array(); + $finalTokenDueToArray = false; } } - ksort($entitiesArr); - - return $entitiesArr; - } - - - /** - * Test if one string starts with another - */ - protected function startsWith($haystack, $needle) { - $length = strlen($needle); - return (substr($haystack, 0, $length) === $needle); - } - - /** - * Converts a parts array from explode function into an array of entities for the i18n text collector - * @return array - */ - protected function entitySpecFromNewParts($parts, $namespace = null) { - // first thing in the parts array will always be the entity - // split fullname into entity parts - //set defaults - $value = ""; - $prio = null; - $comment = null; - - $entityParts = explode('.', $parts[0]); - if(count($entityParts) > 1) { - // templates don't have a custom namespace - $entity = array_pop($entityParts); - // namespace might contain dots, so we explode - $namespace = implode('.',$entityParts); - } else { - $entity = array_pop($entityParts); - $namespace = $namespace; - } - - //find the array (if found, then we are dealing with the new _t syntax - $newSyntax = false; - $offset = 0; - foreach($parts as $p) { - if ($this->startsWith($p,'array')) { //remove everything after (and including) the array - $newSyntax = true; - $parts = array_splice($parts,0,$offset); - break; + foreach($entities as $entity => $spec) { + // call without master language definition + if(!$spec) { + unset($entities[$entity]); + continue; } - $offset++; + + unset($entities[$entity]); + $entities[$this->normalizeEntity($entity, $module)] = $spec; } + ksort($entities); - //2nd part of array is always "string" - if (isset($parts[1])) $value = $parts[1]; - - - //3rd part can either be priority or context, if old or now syntax is used - if (isset($parts[2])) { - if ($newSyntax) { - $prio = 40; //default priority - $comment = $parts[2]; - } else { - if (stripos($parts[2], 'PR_LOW') !== false || - stripos($parts[2], 'PR_MEDIUM') !== false || - stripos($parts[2], 'PR_HIGH') !== false) { //definitely old syntax - $prio = $parts[2]; - } else { //default to new syntax (3rd position is comment/context - $prio = 40; //default priority - $comment = $parts[2]; - } - } - } - - //if 4th position is set then this is old syntax and it is the context - //it would be array in the new syntax and therefore should have already been spliced off - if (isset($parts[3])) { - $comment = $parts[3]; - $prio = $parts[2]; //3rd position is now definitely priority - } - - return array( - "{$namespace}.{$entity}" => array( - $value, - $prio, - $comment - ) - ); + return $entities; } - - public function collectFromTemplate($content, $fileName, $module) { $entities = array(); diff --git a/tests/i18n/i18nTextCollectorTest.php b/tests/i18n/i18nTextCollectorTest.php index 53f483aae..19d7c9fb1 100644 --- a/tests/i18n/i18nTextCollectorTest.php +++ b/tests/i18n/i18nTextCollectorTest.php @@ -58,7 +58,7 @@ _t( _t( 'Test.CONCATENATED2', -"Line "4" and " . +"Line \"4\" and " . "Line 5"); PHP; $this->assertEquals( @@ -87,7 +87,7 @@ SS; $this->assertEquals( $c->collectFromTemplate($html, 'mymodule', 'Test'), array( - 'Test.SINGLEQUOTE' => array('Single Quote',null,null), + 'Test.SINGLEQUOTE' => array('Single Quote'), 'i18nTestModule.NEWMETHODSIG' => array("New _t method signature test",null,null), 'i18nTestModule.INJECTIONS_0' => array("Hello {name} {greeting}. But it is late, {goodbye}", null, null), 'i18nTestModule.INJECTIONS_1' => array("Hello {name} {greeting}. But it is late, {goodbye}", null, null), @@ -328,11 +328,10 @@ PHP; $collectedTranslatables = $c->collectFromCode($php, 'mymodule'); $expectedArray = (array( - 'i18nTestModule.NEWMETHODSIG' => array("New _t method signature test", null, null), - 'i18nTestModule.INJECTIONS1' => array("_DOES_NOT_EXIST", 40, "Hello {name} {greeting}. But it is late, {goodbye}"), - 'i18nTestModule.INJECTIONS2' => array("Hello {name} {greeting}. But it is late, {goodbye}", null, null), - 'i18nTestModule.INJECTIONS3' => array("Hello {name} {greeting}. But it is late, {goodbye}", 40, "New context (this should be ignored)"), - 'i18nTestModule.INJECTIONS4' => array(null, null, null), + 'i18nTestModule.NEWMETHODSIG' => array("New _t method signature test"), + 'i18nTestModule.INJECTIONS1' => array("_DOES_NOT_EXIST", "Hello {name} {greeting}. But it is late, {goodbye}"), + 'i18nTestModule.INJECTIONS2' => array("Hello {name} {greeting}. But it is late, {goodbye}"), + 'i18nTestModule.INJECTIONS3' => array("Hello {name} {greeting}. But it is late, {goodbye}", "New context (this should be ignored)"), )); ksort($expectedArray);