From 8f81e231da1a6df65c635f45af7d4128b77daaad Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sat, 26 Jul 2014 02:39:25 +0400 Subject: [PATCH] Fixes #4453: `yii message/extract` wasn't properly writing to po files in case of multiple categories --- framework/CHANGELOG.md | 1 + .../console/controllers/MessageController.php | 66 +-- framework/views/messageConfig.php | 6 +- .../controllers/BaseMessageControllerTest.php | 346 ++++++++++++++++ .../controllers/MessageControllerTest.php | 387 ------------------ .../controllers/PHPMessageControllerTest.php | 84 ++++ .../controllers/POMessageControllerTest.php | 80 ++++ 7 files changed, 551 insertions(+), 419 deletions(-) create mode 100644 tests/unit/framework/console/controllers/BaseMessageControllerTest.php delete mode 100644 tests/unit/framework/console/controllers/MessageControllerTest.php create mode 100644 tests/unit/framework/console/controllers/PHPMessageControllerTest.php create mode 100644 tests/unit/framework/console/controllers/POMessageControllerTest.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 1b792305c1..fd777f1c1e 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -72,6 +72,7 @@ Yii Framework 2 Change Log - Bug #4409: Upper case letters in subdirectory prefixes of controller IDs were not properly handled (qiangxue) - Bug #4412: Formatter used SI Prefixes for base 1024, now uses binary prefixes (kmindi) - Bug #4427: Formatter could do one division too much (kmindi) +- Bug #4453: `yii message/extract` wasn't properly writing to po files in case of multiple categories (samdark) - Bug: Fixed inconsistent return of `\yii\console\Application::runAction()` (samdark) - Bug: URL encoding for the route parameter added to `\yii\web\UrlManager` (klimov-paul) - Bug: Fixed the bug that requesting protected or private action methods would cause 500 error instead of 404 (qiangxue) diff --git a/framework/console/controllers/MessageController.php b/framework/console/controllers/MessageController.php index 0f03d03f42..6a742d3528 100644 --- a/framework/console/controllers/MessageController.php +++ b/framework/console/controllers/MessageController.php @@ -209,11 +209,11 @@ class MessageController extends Controller } } - echo $savedFlag ? "saved.\n" : "nothing new...skipped.\n"; + echo $savedFlag ? "saved.\n" : "Nothing new...skipped.\n"; echo $removeUnused ? "Deleting obsoleted messages..." : "Updating obsoleted messages..."; if (empty($obsolete)) { - echo "nothing obsoleted...skipped.\n"; + echo "Nothing obsoleted...skipped.\n"; } else { if ($removeUnused) { $db->createCommand() @@ -286,12 +286,10 @@ class MessageController extends Controller foreach ($messages as $category => $msgs) { $file = str_replace("\\", '/', "$dirName/$category.php"); $path = dirname($file); - if (!is_dir($path)) { - mkdir($path, 0755, true); - } + FileHelper::createDirectory($path); $msgs = array_values(array_unique($msgs)); echo "Saving messages to $file...\n"; - $this->saveMessagesCategoryToPHP($msgs, $file, $overwrite, $removeUnused, $sort); + $this->saveMessagesCategoryToPHP($msgs, $file, $overwrite, $removeUnused, $sort, $category); } } @@ -303,23 +301,23 @@ class MessageController extends Controller * @param boolean $overwrite if existing file should be overwritten without backup * @param boolean $removeUnused if obsolete translations should be removed * @param boolean $sort if translations should be sorted + * @param string $category message category */ - protected function saveMessagesCategoryToPHP($messages, $fileName, $overwrite, $removeUnused, $sort) + protected function saveMessagesCategoryToPHP($messages, $fileName, $overwrite, $removeUnused, $sort, $category) { if (is_file($fileName)) { - $translated = require($fileName); + $existingMessages = require($fileName); sort($messages); - ksort($translated); - if (array_keys($translated) == $messages) { - echo "nothing new...skipped.\n"; - - return self::EXIT_CODE_NORMAL; + ksort($existingMessages); + if (array_keys($existingMessages) == $messages) { + echo "Nothing new in \"$category\" category... Nothing to save.\n"; + return; } $merged = []; $untranslated = []; foreach ($messages as $message) { - if (array_key_exists($message, $translated) && strlen($translated[$message]) > 0) { - $merged[$message] = $translated[$message]; + if (array_key_exists($message, $existingMessages) && strlen($existingMessages[$message]) > 0) { + $merged[$message] = $existingMessages[$message]; } else { $untranslated[] = $message; } @@ -330,10 +328,10 @@ class MessageController extends Controller foreach ($untranslated as $message) { $todo[$message] = ''; } - ksort($translated); - foreach ($translated as $message => $translation) { + ksort($existingMessages); + foreach ($existingMessages as $message => $translation) { if (!isset($merged[$message]) && !isset($todo[$message]) && !$removeUnused) { - if (substr_compare($translation, '@@', 0, 2) === 0 && substr_compare($translation, '@@', -2) === 0) { + if (mb_strlen($translation, Yii::$app->charset) >= 2 && substr_compare($translation, '@@', 0, 2) === 0 && substr_compare($translation, '@@', -2) === 0) { $todo[$message] = $translation; } else { $todo[$message] = '@@' . $translation . '@@'; @@ -355,7 +353,6 @@ class MessageController extends Controller } ksort($merged); } - echo "Saved.\n"; $array = VarDumper::export($merged); @@ -383,6 +380,7 @@ return $array; EOD; file_put_contents($fileName, $content); + echo "Saved.\n"; } /** @@ -398,10 +396,7 @@ EOD; protected function saveMessagesToPO($messages, $dirName, $overwrite, $removeUnused, $sort, $catalog) { $file = str_replace("\\", '/', "$dirName/$catalog.po"); - $path = dirname($file); - if (!is_dir($path)) { - mkdir($path, 0755, true); - } + FileHelper::createDirectory(dirname($file)); echo "Saving messages to $file...\n"; $poFile = new GettextPoFile(); @@ -411,6 +406,7 @@ EOD; $notTranslatedYet = []; $todos = []; + $hasSomethingToWrite = false; foreach ($messages as $category => $msgs) { $msgs = array_values(array_unique($msgs)); @@ -420,8 +416,14 @@ EOD; sort($msgs); ksort($existingMessages); if (array_keys($existingMessages) == $msgs) { - echo "Nothing new... skipped.\n"; - return self::EXIT_CODE_NORMAL; + echo "Nothing new in \"$category\" category...\n"; + + sort($msgs); + foreach ($msgs as $message) { + $merged[$category . chr(4) . $message] = ''; + } + ksort($merged); + continue; } // merge existing message translations with new message translations @@ -443,7 +445,7 @@ EOD; // add obsolete unused messages foreach ($existingMessages as $message => $translation) { if (!isset($merged[$category . chr(4) . $message]) && !isset($todos[$category . chr(4) . $message]) && !$removeUnused) { - if (substr($translation, 0, 2) === '@@' && substr($translation, -2) === '@@') { + if (mb_strlen($translation, Yii::$app->charset) >= 2 && substr($translation, 0, 2) === '@@' && substr($translation, -2) === '@@') { $todos[$category . chr(4) . $message] = $translation; } else { $todos[$category . chr(4) . $message] = '@@' . $translation . '@@'; @@ -459,8 +461,6 @@ EOD; if ($overwrite === false) { $file .= '.merged'; } - - echo "Translation merged.\n"; } else { sort($msgs); foreach ($msgs as $message) { @@ -468,8 +468,14 @@ EOD; } ksort($merged); } + echo "Category \"$category\" merged.\n"; + $hasSomethingToWrite = true; + } + if ($hasSomethingToWrite) { + $poFile->save($file, $merged); + echo "Saved.\n"; + } else { + echo "Nothing to save.\n"; } - $poFile->save($file, $merged); - echo "Saved.\n"; } } diff --git a/framework/views/messageConfig.php b/framework/views/messageConfig.php index 22fa10afbd..5afe7c6df4 100644 --- a/framework/views/messageConfig.php +++ b/framework/views/messageConfig.php @@ -15,8 +15,6 @@ return [ // with the existing ones. Defaults to false, which means the new (untranslated) // messages will be separated from the old (translated) ones. 'sort' => false, - // boolean, whether the message file should be overwritten with the merged messages - 'overwrite' => true, // boolean, whether to remove messages that no longer appear in the source code. // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks. 'removeUnused' => false, @@ -47,6 +45,8 @@ return [ 'format' => 'php', // Root directory containing message translations. 'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages', + // boolean, whether the message file should be overwritten with the merged messages + 'overwrite' => true, /* @@ -67,5 +67,7 @@ return [ 'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages', // Name of the file that will be used for translations. 'catalog' => 'messages', + // boolean, whether the message file should be overwritten with the merged messages + 'overwrite' => true, */ ]; diff --git a/tests/unit/framework/console/controllers/BaseMessageControllerTest.php b/tests/unit/framework/console/controllers/BaseMessageControllerTest.php new file mode 100644 index 0000000000..3701704290 --- /dev/null +++ b/tests/unit/framework/console/controllers/BaseMessageControllerTest.php @@ -0,0 +1,346 @@ +mockApplication(); + $this->sourcePath = Yii::getAlias('@yiiunit/runtime/test_source'); + FileHelper::createDirectory($this->sourcePath, 0777); + if (!file_exists($this->sourcePath)) { + $this->markTestIncomplete('Unit tests runtime directory should have writable permissions!'); + } + $this->configFileName = Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . 'message_controller_test_config.php'; + } + + public function tearDown() + { + FileHelper::removeDirectory($this->sourcePath); + if (file_exists($this->configFileName)) { + unlink($this->configFileName); + } + } + + /** + * Creates test message controller instance. + * @return MessageController message command instance. + */ + protected function createMessageController() + { + $module = $this->getMock('yii\\base\\Module', ['fake'], ['console']); + $messageController = new MessageController('message', $module); + $messageController->interactive = false; + + return $messageController; + } + + /** + * Emulates running of the message controller action. + * @param string $actionId id of action to be run. + * @param array $args action arguments. + * @return string command output. + */ + protected function runMessageControllerAction($actionId, array $args = []) + { + $controller = $this->createMessageController(); + ob_start(); + ob_implicit_flush(false); + $controller->run($actionId, $args); + + return ob_get_clean(); + } + + /** + * Creates message command config file named as [[configFileName]]. + * @param array $config message command config. + */ + protected function saveConfigFile(array $config) + { + if (file_exists($this->configFileName)) { + unlink($this->configFileName); + } + $fileContent = 'configFileName, $fileContent); + } + + /** + * Creates source file with given content + * @param string $content file content + * @return string path to source file + */ + protected function createSourceFile($content) + { + $fileName = $this->sourcePath . DIRECTORY_SEPARATOR . md5(uniqid()) . '.php'; + file_put_contents($fileName, $content); + return $fileName; + } + + /** + * Saves messages + * + * @param array $messages + * @param string $category + */ + abstract protected function saveMessages($messages, $category); + + /** + * Loads messages + * + * @param string $category + * @return array + */ + abstract protected function loadMessages($category); + + /** + * @return array default config + */ + abstract protected function getDefaultConfig(); + + /** + * Returns config + * + * @param array $additionalConfig + * @return array + */ + protected function getConfig($additionalConfig = []) + { + return array_merge($this->getDefaultConfig(), $additionalConfig); + } + + // Tests: + + public function testActionConfig() + { + $configFileName = $this->configFileName; + $out = $this->runMessageControllerAction('config', [$configFileName]); + $this->assertTrue(file_exists($configFileName), "Unable to create config file from template. Command output:\n\n" . $out); + } + + public function testConfigFileNotExist() + { + $this->setExpectedException('yii\\console\\Exception'); + $this->runMessageControllerAction('extract', ['not_existing_file.php']); + } + + public function testCreateTranslation() + { + $category = 'test_category1'; + $message = 'test message'; + $sourceFileContent = "Yii::t('{$category}', '{$message}');"; + $this->createSourceFile($sourceFileContent); + + $this->saveConfigFile($this->getConfig()); + $out = $this->runMessageControllerAction('extract', [$this->configFileName]); + + $messages = $this->loadMessages($category); + $this->assertArrayHasKey($message, $messages, "\"$message\" is missing in translation file. Command output:\n\n" . $out); + } + + /** + * @depends testCreateTranslation + */ + public function testNothingToSave() + { + $category = 'test_category2'; + $message = 'test message'; + $sourceFileContent = "Yii::t('{$category}', '{$message}')"; + $this->createSourceFile($sourceFileContent); + + $this->saveConfigFile($this->getConfig()); + $out = $this->runMessageControllerAction('extract', [$this->configFileName]); + $out .= $this->runMessageControllerAction('extract', [$this->configFileName]); + + $this->assertTrue(strpos($out, 'Nothing to save') !== false, "Controller should respond with \"Nothing to save\" if there's nothing to update. Command output:\n\n" . $out); + } + + /** + * @depends testCreateTranslation + */ + public function testMerge() + { + $category = 'test_category3'; + + $existingMessage = 'test existing message'; + $existingMessageTranslation = 'test existing message translation'; + $this->saveMessages( + [$existingMessage => $existingMessageTranslation], + $category + ); + + $newMessage = 'test new message'; + $sourceFileContent = "Yii::t('{$category}', '{$existingMessage}');"; + $sourceFileContent .= "Yii::t('{$category}', '{$newMessage}');"; + $this->createSourceFile($sourceFileContent); + + $this->saveConfigFile($this->getConfig()); + $out = $this->runMessageControllerAction('extract', [$this->configFileName]); + + $messages = $this->loadMessages($category); + $this->assertArrayHasKey($newMessage, $messages, "Unable to add new message: \"$newMessage\". Command output:\n\n" . $out); + $this->assertArrayHasKey($existingMessage, $messages, "Unable to keep existing message: \"$existingMessage\". Command output:\n\n" . $out); + $this->assertEquals('', $messages[$newMessage], "Wrong new message content. Command output:\n\n" . $out); + $this->assertEquals($existingMessageTranslation, $messages[$existingMessage], "Unable to keep existing message content. Command output:\n\n" . $out); + } + + /** + * @depends testMerge + */ + public function testMarkObosoleteMessages() + { + $category = 'category'; + + $obsoleteMessage = 'obsolete message'; + $obsoleteTranslation = 'obsolete translation'; + $this->saveMessages([$obsoleteMessage => $obsoleteTranslation], $category); + + $sourceFileContent = "Yii::t('{$category}', 'any new message');"; + $this->createSourceFile($sourceFileContent); + + $this->saveConfigFile($this->getConfig(['removeUnused' => false])); + $out = $this->runMessageControllerAction('extract', [$this->configFileName]); + + $messages = $this->loadMessages($category); + + $this->assertArrayHasKey($obsoleteMessage, $messages, "Obsolete message should not be removed. Command output:\n\n" . $out); + $this->assertEquals('@@' . $obsoleteTranslation . '@@', $messages[$obsoleteMessage], "Obsolete message was not marked properly. Command output:\n\n" . $out); + } + + /** + * @depends testMerge + */ + public function removeObosoleteMessages() + { + $category = 'category'; + + $obsoleteMessage = 'obsolete message'; + $obsoleteTranslation = 'obsolete translation'; + $this->saveMessages([$obsoleteMessage => $obsoleteTranslation], $category); + + $sourceFileContent = "Yii::t('{$category}', 'any new message');"; + $this->createSourceFile($sourceFileContent); + + $this->saveConfigFile($this->getConfig(['removeUnused' => true])); + $out = $this->runMessageControllerAction('extract', [$this->configFileName]); + + $messages = $this->loadMessages($category); + + $this->assertArrayHasKey($obsoleteMessage, $messages, "Obsolete message should be removed. Command output:\n\n" . $out); + } + + /** + * @depends testMerge + */ + public function testMergeWithContentZero() + { + $category = 'test_category5'; + + $zeroMessage = 'test zero message'; + $zeroMessageContent = '0'; + $falseMessage = 'test false message'; + $falseMessageContent = 'false'; + $this->saveMessages([ + $zeroMessage => $zeroMessageContent, + $falseMessage => $falseMessageContent, + ], $category); + + $newMessage = 'test new message'; + $sourceFileContent = "Yii::t('{$category}', '{$zeroMessage}')"; + $sourceFileContent .= "Yii::t('{$category}', '{$falseMessage}')"; + $sourceFileContent .= "Yii::t('{$category}', '{$newMessage}')"; + $this->createSourceFile($sourceFileContent); + + $this->saveConfigFile($this->getConfig()); + $out = $this->runMessageControllerAction('extract', [$this->configFileName]); + + $messages = $this->loadMessages($category); + $this->assertTrue($zeroMessageContent === $messages[$zeroMessage], "Message content \"0\" is lost. Command output:\n\n" . $out); + $this->assertTrue($falseMessageContent === $messages[$falseMessage], "Message content \"false\" is lost. Command output:\n\n" . $out); + } + + /** + * @depends testCreateTranslation + */ + public function testMultipleTranslators() + { + $category = 'test_category6'; + + $translators = [ + 'Yii::t', + 'Custom::translate', + ]; + + $sourceMessages = [ + 'first message', + 'second message', + ]; + $sourceFileContent = ''; + foreach ($sourceMessages as $key => $message) { + $sourceFileContent .= $translators[$key] . "('{$category}', '{$message}');\n"; + } + $this->createSourceFile($sourceFileContent); + + $this->saveConfigFile($this->getConfig(['translator' => $translators])); + $this->runMessageControllerAction('extract', [$this->configFileName]); + + $messages = $this->loadMessages($category); + + foreach ($sourceMessages as $sourceMessage) { + $this->assertArrayHasKey($sourceMessage, $messages); + } + } + + /** + * @depends testCreateTranslation + */ + public function testMultipleCategories() + { + $category1 = 'category1'; + $category2 = 'category2'; + + $message1 = 'message1'; + $message2 = 'message2'; + $message3 = 'message3'; + + $this->saveConfigFile($this->getConfig(['removeUnused' => true])); + + // Generate initial translation + $sourceFileContent = "Yii::t('{$category1}', '{$message1}'); Yii::t('{$category2}', '{$message2}');"; + $source = $this->createSourceFile($sourceFileContent); + $out = $this->runMessageControllerAction('extract', [$this->configFileName]); + unlink($source); + + $messages1 = $this->loadMessages($category1); + $messages2 = $this->loadMessages($category2); + + $this->assertArrayHasKey($message1, $messages1, "message1 not found in category1. Command output:\n\n" . $out); + $this->assertArrayHasKey($message2, $messages2, "message2 not found in category2. Command output:\n\n" . $out); + $this->assertArrayNotHasKey($message3, $messages2, "message3 found in category2. Command output:\n\n" . $out); + + // Change source code, run translation again + $sourceFileContent = "Yii::t('{$category1}', '{$message1}'); Yii::t('{$category2}', '{$message3}');"; + $source = $this->createSourceFile($sourceFileContent); + $out .= "\n" . $this->runMessageControllerAction('extract', [$this->configFileName]); + unlink($source); + + $messages1 = $this->loadMessages($category1); + $messages2 = $this->loadMessages($category2); + $this->assertArrayHasKey($message1, $messages1, "message1 not found in category1. Command output:\n\n" . $out); + $this->assertArrayHasKey($message3, $messages2, "message3 not found in category2. Command output:\n\n" . $out); + $this->assertArrayNotHasKey($message2, $messages2, "message2 found in category2. Command output:\n\n" . $out); + } +} diff --git a/tests/unit/framework/console/controllers/MessageControllerTest.php b/tests/unit/framework/console/controllers/MessageControllerTest.php deleted file mode 100644 index 38e3f69dbe..0000000000 --- a/tests/unit/framework/console/controllers/MessageControllerTest.php +++ /dev/null @@ -1,387 +0,0 @@ -mockApplication(); - $this->sourcePath = Yii::getAlias('@yiiunit/runtime/test_source'); - $this->createDir($this->sourcePath); - if (!file_exists($this->sourcePath)) { - $this->markTestIncomplete('Unit tests runtime directory should have writable permissions!'); - } - $this->messagePath = Yii::getAlias('@yiiunit/runtime/test_messages'); - $this->createDir($this->messagePath); - $this->configFileName = Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . 'message_controller_test_config.php'; - } - - public function tearDown() - { - $this->removeDir($this->sourcePath); - $this->removeDir($this->messagePath); - if (file_exists($this->configFileName)) { - unlink($this->configFileName); - } - } - - /** - * Creates directory. - * @param $dirName directory full name - */ - protected function createDir($dirName) - { - if (!file_exists($dirName)) { - mkdir($dirName, 0777, true); - } - } - - /** - * Removes directory. - * @param $dirName directory full name - */ - protected function removeDir($dirName) - { - if (!empty($dirName) && file_exists($dirName)) { - $this->removeFileSystemObject($dirName); - } - } - - /** - * Removes file system object: directory or file. - * @param string $fileSystemObjectFullName file system object full name. - */ - protected function removeFileSystemObject($fileSystemObjectFullName) - { - if (!is_dir($fileSystemObjectFullName)) { - unlink($fileSystemObjectFullName); - } else { - $dirHandle = opendir($fileSystemObjectFullName); - while (($fileSystemObjectName = readdir($dirHandle)) !== false) { - if ($fileSystemObjectName === '.' || $fileSystemObjectName === '..') { - continue; - } - $this->removeFileSystemObject($fileSystemObjectFullName . DIRECTORY_SEPARATOR . $fileSystemObjectName); - } - closedir($dirHandle); - rmdir($fileSystemObjectFullName); - } - } - - /** - * Creates test message controller instance. - * @return MessageController message command instance. - */ - protected function createMessageController() - { - $module = $this->getMock('yii\\base\\Module', ['fake'], ['console']); - $messageController = new MessageController('message', $module); - $messageController->interactive = false; - - return $messageController; - } - - /** - * Emulates running of the message controller action. - * @param string $actionId id of action to be run. - * @param array $args action arguments. - * @return string command output. - */ - protected function runMessageControllerAction($actionId, array $args = []) - { - $controller = $this->createMessageController(); - ob_start(); - ob_implicit_flush(false); - $controller->run($actionId, $args); - - return ob_get_clean(); - } - - /** - * Creates message command config file named as [[configFileName]]. - * @param array $config message command config. - */ - protected function composeConfigFile(array $config) - { - if (file_exists($this->configFileName)) { - unlink($this->configFileName); - } - $fileContent = 'configFileName, $fileContent); - } - - /** - * Creates source file with given content - * @param string $content file content - * @param string|null $name file self name - */ - protected function createSourceFile($content, $name = null) - { - if (empty($name)) { - $name = md5(uniqid()) . '.php'; - } - file_put_contents($this->sourcePath . DIRECTORY_SEPARATOR . $name, $content); - } - - /** - * Creates message file with given messages. - * @param string $name file name - * @param array $messages messages. - */ - protected function createMessageFile($name, array $messages = []) - { - $fileName = $this->messagePath . DIRECTORY_SEPARATOR . $name; - if (file_exists($fileName)) { - unlink($fileName); - } else { - $dirName = dirname($fileName); - if (!file_exists($dirName)) { - mkdir($dirName, 0777, true); - } - } - $fileContent = 'configFileName; - $this->runMessageControllerAction('config', [$configFileName]); - $this->assertTrue(file_exists($configFileName), 'Unable to create config file template!'); - } - - public function testConfigFileNotExist() - { - $this->setExpectedException('yii\\console\\Exception'); - $this->runMessageControllerAction('extract', ['not_existing_file.php']); - } - - public function testCreateTranslation() - { - $language = 'en'; - - $category = 'test_category1'; - $message = 'test message'; - $sourceFileContent = "Yii::t('{$category}', '{$message}')"; - $this->createSourceFile($sourceFileContent); - - $this->composeConfigFile([ - 'languages' => [$language], - 'sourcePath' => $this->sourcePath, - 'messagePath' => $this->messagePath, - ]); - $this->runMessageControllerAction('extract', [$this->configFileName]); - - $this->assertTrue(file_exists($this->messagePath . DIRECTORY_SEPARATOR . $language), 'No language dir created!'); - $messageFileName = $this->messagePath . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . $category . '.php'; - $this->assertTrue(file_exists($messageFileName), 'No message file created!'); - $messages = require($messageFileName); - $this->assertTrue(is_array($messages), 'Unable to compose messages!'); - $this->assertTrue(array_key_exists($message, $messages), 'Source message is missing!'); - } - - /** - * @depends testCreateTranslation - */ - public function testNothingNew() - { - $language = 'en'; - - $category = 'test_category2'; - $message = 'test message'; - $sourceFileContent = "Yii::t('{$category}', '{$message}')"; - $this->createSourceFile($sourceFileContent); - - $this->composeConfigFile([ - 'languages' => [$language], - 'sourcePath' => $this->sourcePath, - 'messagePath' => $this->messagePath, - ]); - $this->runMessageControllerAction('extract', [$this->configFileName]); - - $messageFileName = $this->messagePath . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . $category . '.php'; - - // check file not overwritten: - $messageFileContent = file_get_contents($messageFileName); - $messageFileContent .= '// some not generated by command content'; - file_put_contents($messageFileName, $messageFileContent); - - $this->runMessageControllerAction('extract', [$this->configFileName]); - - $this->assertEquals($messageFileContent, file_get_contents($messageFileName)); - } - - /** - * @depends testCreateTranslation - */ - public function testMerge() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Can not test on HHVM because modified files can not be reloaded.'); - } - - $language = 'en'; - $category = 'test_category3'; - $messageFileName = $language . DIRECTORY_SEPARATOR . $category . '.php'; - - $existingMessage = 'test existing message'; - $existingMessageContent = 'test existing message content'; - $this->createMessageFile($messageFileName, [ - $existingMessage => $existingMessageContent - ]); - - $newMessage = 'test new message'; - $sourceFileContent = "Yii::t('{$category}', '{$existingMessage}')"; - $sourceFileContent .= "Yii::t('{$category}', '{$newMessage}')"; - $this->createSourceFile($sourceFileContent); - - $this->composeConfigFile([ - 'languages' => [$language], - 'sourcePath' => $this->sourcePath, - 'messagePath' => $this->messagePath, - 'overwrite' => true, - ]); - $commandOutput = $this->runMessageControllerAction('extract', [$this->configFileName]); - - $messages = require($this->messagePath . DIRECTORY_SEPARATOR . $messageFileName); - $this->assertTrue(array_key_exists($newMessage, $messages), 'Unable to add new message: "' . $newMessage . '". Command output was:' . "\n" . $commandOutput); - $this->assertTrue(array_key_exists($existingMessage, $messages), 'Unable to keep existing message: "' . $existingMessage . '". Command output was:' . "\n" . $commandOutput); - $this->assertEquals('', $messages[$newMessage], 'Wrong new message content!. Command output was:\n' . $commandOutput); - $this->assertEquals($existingMessageContent, $messages[$existingMessage], 'Unable to keep existing message content!. Command output was:' . "\n" . $commandOutput); - } - - /** - * @depends testMerge - */ - public function testNoLongerNeedTranslation() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Can not test on HHVM because modified files can not be reloaded.'); - } - - $language = 'en'; - $category = 'test_category4'; - $messageFileName = $language . DIRECTORY_SEPARATOR . $category . '.php'; - - $oldMessage = 'test old message'; - $oldMessageContent = 'test old message content'; - $this->createMessageFile($messageFileName, [ - $oldMessage => $oldMessageContent - ]); - - $sourceFileContent = "Yii::t('{$category}', 'some new message')"; - $this->createSourceFile($sourceFileContent); - - $this->composeConfigFile([ - 'languages' => [$language], - 'sourcePath' => $this->sourcePath, - 'messagePath' => $this->messagePath, - 'overwrite' => true, - 'removeUnused' => false, - ]); - $this->runMessageControllerAction('extract', [$this->configFileName]); - - $messages = require($this->messagePath . DIRECTORY_SEPARATOR . $messageFileName); - - $this->assertTrue(array_key_exists($oldMessage, $messages), 'No longer needed message removed!'); - $this->assertEquals('@@' . $oldMessageContent . '@@', $messages[$oldMessage], 'No longer needed message content does not marked properly!'); - } - - /** - * @depends testMerge - */ - public function testMergeWithContentZero() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Can not test on HHVM because modified files can not be reloaded.'); - } - - $language = 'en'; - $category = 'test_category5'; - $messageFileName = $language . DIRECTORY_SEPARATOR . $category . '.php'; - - $zeroMessage = 'test zero message'; - $zeroMessageContent = '0'; - $falseMessage = 'test false message'; - $falseMessageContent = 'false'; - $this->createMessageFile($messageFileName, [ - $zeroMessage => $zeroMessageContent, - $falseMessage => $falseMessageContent, - ]); - - $newMessage = 'test new message'; - $sourceFileContent = "Yii::t('{$category}', '{$zeroMessage}')"; - $sourceFileContent .= "Yii::t('{$category}', '{$falseMessage}')"; - $sourceFileContent .= "Yii::t('{$category}', '{$newMessage}')"; - $this->createSourceFile($sourceFileContent); - - $this->composeConfigFile([ - 'languages' => [$language], - 'sourcePath' => $this->sourcePath, - 'messagePath' => $this->messagePath, - 'overwrite' => true, - ]); - $this->runMessageControllerAction('extract', [$this->configFileName]); - - $messages = require($this->messagePath . DIRECTORY_SEPARATOR . $messageFileName); - $this->assertTrue($zeroMessageContent === $messages[$zeroMessage], 'Message content "0" is lost!'); - $this->assertTrue($falseMessageContent === $messages[$falseMessage], 'Message content "false" is lost!'); - } - - /** - * @depends testCreateTranslation - */ - public function testMultiplyTranslators() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Can not test on HHVM because modified files can not be reloaded.'); - } - - $language = 'en'; - $category = 'test_category6'; - - $translators = [ - 'Yii::t', - 'Custom::translate', - ]; - - $sourceMessages = [ - 'first message', - 'second message', - ]; - $sourceFileContent = ''; - foreach ($sourceMessages as $key => $message) { - $sourceFileContent .= $translators[$key] . "('{$category}', '{$message}');\n"; - } - $this->createSourceFile($sourceFileContent); - - $this->composeConfigFile([ - 'languages' => [$language], - 'sourcePath' => $this->sourcePath, - 'messagePath' => $this->messagePath, - 'translator' => $translators, - ]); - $this->runMessageControllerAction('extract', [$this->configFileName]); - - $messageFileName = $this->messagePath . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . $category . '.php'; - $messages = require($messageFileName); - - foreach ($sourceMessages as $sourceMessage) { - $this->assertTrue(array_key_exists($sourceMessage, $messages)); - } - } -} diff --git a/tests/unit/framework/console/controllers/PHPMessageControllerTest.php b/tests/unit/framework/console/controllers/PHPMessageControllerTest.php new file mode 100644 index 0000000000..20ddf1d78a --- /dev/null +++ b/tests/unit/framework/console/controllers/PHPMessageControllerTest.php @@ -0,0 +1,84 @@ +messagePath = Yii::getAlias('@yiiunit/runtime/test_messages'); + FileHelper::createDirectory($this->messagePath, 0777); + } + + public function tearDown() + { + parent::tearDown(); + FileHelper::removeDirectory($this->messagePath); + } + + /** + * @inheritdoc + */ + protected function getDefaultConfig() + { + return [ + 'format' => 'php', + 'languages' => [$this->language], + 'sourcePath' => $this->sourcePath, + 'messagePath' => $this->messagePath, + 'overwrite' => true, + ]; + } + + /** + * @param string $category + * @return string message file path + */ + protected function getMessageFilePath($category) + { + return $this->messagePath . '/' . $this->language . '/' . $category . '.php'; + } + + /** + * @inheritdoc + */ + protected function saveMessages($messages, $category) + { + $fileName = $this->getMessageFilePath($category); + if (file_exists($fileName)) { + unlink($fileName); + } else { + $dirName = dirname($fileName); + if (!file_exists($dirName)) { + mkdir($dirName, 0777, true); + } + } + $fileContent = 'markTestSkipped('Can not test on HHVM because require is cached.'); + } + + $messageFilePath = $this->getMessageFilePath($category); + $this->assertTrue(file_exists($messageFilePath), "There's no message file $messageFilePath!"); + return require $messageFilePath; + } +} \ No newline at end of file diff --git a/tests/unit/framework/console/controllers/POMessageControllerTest.php b/tests/unit/framework/console/controllers/POMessageControllerTest.php new file mode 100644 index 0000000000..95d4e2fae5 --- /dev/null +++ b/tests/unit/framework/console/controllers/POMessageControllerTest.php @@ -0,0 +1,80 @@ +messagePath = Yii::getAlias('@yiiunit/runtime/test_messages'); + FileHelper::createDirectory($this->messagePath, 0777); + } + + public function tearDown() + { + parent::tearDown(); + FileHelper::removeDirectory($this->messagePath); + } + + /** + * @inheritdoc + */ + protected function getDefaultConfig() + { + return [ + 'format' => 'po', + 'languages' => [$this->language], + 'sourcePath' => $this->sourcePath, + 'messagePath' => $this->messagePath, + 'overwrite' => true, + ]; + } + + /** + * @return string message file path + */ + protected function getMessageFilePath() + { + return $this->messagePath . '/' . $this->language . '/' . $this->catalog . '.po'; + } + + /** + * @inheritdoc + */ + protected function saveMessages($messages, $category) + { + $messageFilePath = $this->getMessageFilePath(); + FileHelper::createDirectory(dirname($messageFilePath), 0777); + $gettext = new GettextPoFile(); + + $data = []; + foreach ($messages as $message => $translation) { + $data[$category . chr(4) . $message] = $translation; + } + + $gettext->save($messageFilePath, $data); + } + + /** + * @inheritdoc + */ + protected function loadMessages($category) + { + $messageFilePath = $this->getMessageFilePath(); + $this->assertTrue(file_exists($messageFilePath), "There's no message file $messageFilePath!"); + + $gettext = new GettextPoFile(); + return $gettext->load($messageFilePath, $category); + } +} \ No newline at end of file