Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
db3bfd09
Commit
db3bfd09
authored
Jun 11, 2013
by
Qiang Xue
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #528 from klimov-paul/message-command
Compose Message Command
parents
d19446ba
014053f0
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
452 additions
and
23 deletions
+452
-23
MessageController.php
framework/yii/console/controllers/MessageController.php
+83
-23
MessageControllerTest.php
...t/framework/console/controllers/MessageControllerTest.php
+369
-0
No files found.
framework/yii/console/controllers/MessageController.php
View file @
db3bfd09
...
...
@@ -9,23 +9,37 @@
namespace
yii\console\controllers
;
use
yii\console\Controller
;
use
yii\console\Exception
;
use
yii\helpers\FileHelper
;
/**
* This command extracts messages to be translated from source files.
* The extracted messages are saved as PHP message source files
* under the specified directory.
*
* Usage:
* 1. Create a configuration file using 'template' action:
* yii message/template /path/to/myapp/messages/config.php
* 2. Edit the created config file, adjusting it for your web application needs.
* 3. Run the 'generate' action, using created config:
* yii message /path/to/myapp/messages/config.php
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class
MessageController
extends
Controller
{
/**
* @var string controller default action ID.
*/
public
$defaultAction
=
'generate'
;
/**
* Searches for messages to be translated in the specified
* source files and compiles them into PHP arrays as message source.
*
* @param string $config the path of the configuration file. You can find
* an example in framework/messages/config.php.
* @throws \yii\console\Exception on failure.
*
* The file can be placed anywhere and must be a valid PHP script which
* returns an array of name-value pairs. Each name-value pair represents
...
...
@@ -48,35 +62,36 @@ class MessageController extends Controller
* directory 'sourcePath/a/b'.
* - translator: the name of the function for translating messages.
* Defaults to 'Yii::t'. This is used as a mark to find messages to be
* translated.
* translated. Accepts both string for single function name or array for
* multiple function names.
* - overwrite: if message file must be overwritten with the merged messages.
* - removeOld: if message no longer needs translation it will be removed,
* instead of being enclosed between a pair of '@@' marks.
* - sort: sort messages by key when merging, regardless of their translation
* state (new, obsolete, translated.)
*/
public
function
action
Index
(
$config
)
public
function
action
Generate
(
$config
)
{
if
(
!
is_file
(
$config
))
{
$this
->
usageError
(
"the configuration file
{
$config
}
does not exist."
);
throw
new
Exception
(
"the configuration file
{
$config
}
does not exist."
);
}
$config
=
require
_once
(
$config
);
$config
=
require
(
$config
);
$translator
=
'Yii::t'
;
$translator
=
'Yii::t'
;
extract
(
$config
);
if
(
!
isset
(
$sourcePath
,
$messagePath
,
$languages
))
{
$this
->
usageError
(
'The configuration file must specify "sourcePath", "messagePath" and "languages".'
);
throw
new
Exception
(
'The configuration file must specify "sourcePath", "messagePath" and "languages".'
);
}
if
(
!
is_dir
(
$sourcePath
))
{
$this
->
usageError
(
"The source path
$sourcePath
is not a valid directory."
);
throw
new
Exception
(
"The source path
{
$sourcePath
}
is not a valid directory."
);
}
if
(
!
is_dir
(
$messagePath
))
{
$this
->
usageError
(
"The message path
$messagePath
is not a valid directory."
);
throw
new
Exception
(
"The message path
{
$messagePath
}
is not a valid directory."
);
}
if
(
empty
(
$languages
))
{
$this
->
usageError
(
"Languages cannot be empty."
);
throw
new
Exception
(
"Languages cannot be empty."
);
}
if
(
!
isset
(
$overwrite
))
{
...
...
@@ -96,7 +111,7 @@ class MessageController extends Controller
if
(
isset
(
$exclude
))
{
$options
[
'exclude'
]
=
$exclude
;
}
$files
=
C
FileHelper
::
findFiles
(
realpath
(
$sourcePath
),
$options
);
$files
=
FileHelper
::
findFiles
(
realpath
(
$sourcePath
),
$options
);
$messages
=
array
();
foreach
(
$files
as
$file
)
{
...
...
@@ -126,18 +141,23 @@ class MessageController extends Controller
{
echo
"Extracting messages from
$fileName
...
\n
"
;
$subject
=
file_get_contents
(
$fileName
);
$n
=
preg_match_all
(
'/\b'
.
$translator
.
'\s*\(\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*,\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*[,\)]/s'
,
$subject
,
$matches
,
PREG_SET_ORDER
);
$messages
=
array
();
for
(
$i
=
0
;
$i
<
$n
;
++
$i
)
{
if
((
$pos
=
strpos
(
$matches
[
$i
][
1
],
'.'
))
!==
false
)
{
$category
=
substr
(
$matches
[
$i
][
1
],
$pos
+
1
,
-
1
);
}
else
{
$category
=
substr
(
$matches
[
$i
][
1
],
1
,
-
1
);
if
(
!
is_array
(
$translator
))
{
$translator
=
array
(
$translator
);
}
foreach
(
$translator
as
$currentTranslator
)
{
$n
=
preg_match_all
(
'/\b'
.
$currentTranslator
.
'\s*\(\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*,\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*[,\)]/s'
,
$subject
,
$matches
,
PREG_SET_ORDER
);
for
(
$i
=
0
;
$i
<
$n
;
++
$i
)
{
if
((
$pos
=
strpos
(
$matches
[
$i
][
1
],
'.'
))
!==
false
)
{
$category
=
substr
(
$matches
[
$i
][
1
],
$pos
+
1
,
-
1
);
}
else
{
$category
=
substr
(
$matches
[
$i
][
1
],
1
,
-
1
);
}
$message
=
$matches
[
$i
][
2
];
$messages
[
$category
][]
=
eval
(
"return
$message
;"
);
// use eval to eliminate quote escape
}
$message
=
$matches
[
$i
][
2
];
$messages
[
$category
][]
=
eval
(
"return
$message
;"
);
// use eval to eliminate quote escape
}
return
$messages
;
}
...
...
@@ -165,7 +185,7 @@ class MessageController extends Controller
$merged
=
array
();
$untranslated
=
array
();
foreach
(
$messages
as
$message
)
{
if
(
!
empty
(
$translated
[
$message
])
)
{
if
(
array_key_exists
(
$message
,
$translated
)
&&
strlen
(
$translated
[
$message
])
>
0
)
{
$merged
[
$message
]
=
$translated
[
$message
];
}
else
{
$untranslated
[]
=
$message
;
...
...
@@ -209,7 +229,7 @@ class MessageController extends Controller
/**
* Message translations.
*
* This file is automatically generated by 'yii
message
' command.
* This file is automatically generated by 'yii
{$this->id}
' command.
* It contains the localizable messages extracted from source code.
* You may modify this file by translating the extracted messages.
*
...
...
@@ -221,11 +241,51 @@ class MessageController extends Controller
* Message string can be used with plural forms format. Check i18n section
* of the guide for details.
*
* NOTE
,
this file must be saved in UTF-8 encoding.
* NOTE
:
this file must be saved in UTF-8 encoding.
*/
return $array;
EOD;
file_put_contents
(
$fileName
,
$content
);
}
/**
* Creates template of configuration file for [[actionGenerate]].
* @param string $configFile output file name.
* @throws \yii\console\Exception on failure.
*/
public
function
actionTemplate
(
$configFile
)
{
$template
=
<<<EOD
<?php
/**
* Configuration file for the "yii {$this->id}" console command.
*/
return array(
'sourcePath' => __DIR__,
'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
'languages' => array(),
'fileTypes' => array('php'),
'overwrite' => true,
'exclude' => array(
'.svn',
'.gitignore',
'.gitkeep',
'.hgignore',
'.hgkeep',
'/messages',
),
);
EOD;
if
(
file_exists
(
$configFile
))
{
if
(
!
$this
->
confirm
(
"File '
{
$configFile
}
' already exists. Do you wish to overwrite it?"
))
{
return
;
}
}
if
(
!
file_put_contents
(
$configFile
,
$template
))
{
throw
new
Exception
(
"Unable to write template file '
{
$configFile
}
'."
);
}
else
{
echo
"Configuration file template created at '
{
$configFile
}
'.
\n\n
"
;
}
}
}
tests/unit/framework/console/controllers/MessageControllerTest.php
0 → 100644
View file @
db3bfd09
<?php
use
yiiunit\TestCase
;
use
yii\console\controllers\MessageController
;
/**
* Unit test for [[\yii\console\controllers\MessageController]].
* @see MessageController
*/
class
MessageControllerTest
extends
TestCase
{
protected
$sourcePath
=
''
;
protected
$messagePath
=
''
;
protected
$configFileName
=
''
;
public
function
setUp
()
{
$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'
,
array
(
'fake'
),
array
(
'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
=
array
())
{
$controller
=
$this
->
createMessageController
();
ob_start
();
ob_implicit_flush
(
false
);
$params
=
array
(
\yii\console\Request
::
ANONYMOUS_PARAMS
=>
$args
);
$controller
->
run
(
$actionId
,
$params
);
return
ob_get_clean
();
}
/**
* Creates message command config file at {@link configFileName}
* @param array $config message command config.
*/
protected
function
composeConfigFile
(
array
$config
)
{
if
(
file_exists
(
$this
->
configFileName
))
{
unlink
(
$this
->
configFileName
);
}
$fileContent
=
'<?php return '
.
var_export
(
$config
,
true
)
.
';'
;
file_put_contents
(
$this
->
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
=
array
())
{
$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
=
'<?php return '
.
var_export
(
$messages
,
true
)
.
';'
;
file_put_contents
(
$fileName
,
$fileContent
);
}
// Tests:
public
function
testActionTemplate
()
{
$configFileName
=
$this
->
configFileName
;
$this
->
runMessageControllerAction
(
'template'
,
array
(
$configFileName
));
$this
->
assertTrue
(
file_exists
(
$configFileName
),
'Unable to create config file template!'
);
}
public
function
testConfigFileNotExist
()
{
$this
->
setExpectedException
(
'yii\\console\\Exception'
);
$this
->
runMessageControllerAction
(
'generate'
,
array
(
'not_existing_file.php'
));
}
public
function
testCreateTranslation
()
{
$language
=
'en'
;
$category
=
'test_category'
;
$message
=
'test message'
;
$sourceFileContent
=
"Yii::t('
{
$category
}
', '
{
$message
}
')"
;
$this
->
createSourceFile
(
$sourceFileContent
);
$this
->
composeConfigFile
(
array
(
'languages'
=>
array
(
$language
),
'sourcePath'
=>
$this
->
sourcePath
,
'messagePath'
=>
$this
->
messagePath
,
));
$this
->
runMessageControllerAction
(
'generate'
,
array
(
$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_category'
;
$message
=
'test message'
;
$sourceFileContent
=
"Yii::t('
{
$category
}
', '
{
$message
}
')"
;
$this
->
createSourceFile
(
$sourceFileContent
);
$this
->
composeConfigFile
(
array
(
'languages'
=>
array
(
$language
),
'sourcePath'
=>
$this
->
sourcePath
,
'messagePath'
=>
$this
->
messagePath
,
));
$this
->
runMessageControllerAction
(
'generate'
,
array
(
$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
(
'generate'
,
array
(
$this
->
configFileName
));
$this
->
assertEquals
(
$messageFileContent
,
file_get_contents
(
$messageFileName
));
}
/**
* @depends testCreateTranslation
*/
public
function
testMerge
()
{
$language
=
'en'
;
$category
=
'test_category'
;
$messageFileName
=
$language
.
DIRECTORY_SEPARATOR
.
$category
.
'.php'
;
$existingMessage
=
'test existing message'
;
$existingMessageContent
=
'test existing message content'
;
$this
->
createMessageFile
(
$messageFileName
,
array
(
$existingMessage
=>
$existingMessageContent
));
$newMessage
=
'test new message'
;
$sourceFileContent
=
"Yii::t('
{
$category
}
', '
{
$existingMessage
}
')"
;
$sourceFileContent
.=
"Yii::t('
{
$category
}
', '
{
$newMessage
}
')"
;
$this
->
createSourceFile
(
$sourceFileContent
);
$this
->
composeConfigFile
(
array
(
'languages'
=>
array
(
$language
),
'sourcePath'
=>
$this
->
sourcePath
,
'messagePath'
=>
$this
->
messagePath
,
'overwrite'
=>
true
,
));
$this
->
runMessageControllerAction
(
'generate'
,
array
(
$this
->
configFileName
));
$messages
=
require
(
$this
->
messagePath
.
DIRECTORY_SEPARATOR
.
$messageFileName
);
$this
->
assertTrue
(
array_key_exists
(
$newMessage
,
$messages
),
'Unable to add new message!'
);
$this
->
assertTrue
(
array_key_exists
(
$existingMessage
,
$messages
),
'Unable to keep existing message!'
);
$this
->
assertEquals
(
''
,
$messages
[
$newMessage
],
'Wrong new message content!'
);
$this
->
assertEquals
(
$existingMessageContent
,
$messages
[
$existingMessage
],
'Unable to keep existing message content!'
);
}
/**
* @depends testMerge
*/
public
function
testNoLongerNeedTranslation
()
{
$language
=
'en'
;
$category
=
'test_category'
;
$messageFileName
=
$language
.
DIRECTORY_SEPARATOR
.
$category
.
'.php'
;
$oldMessage
=
'test old message'
;
$oldMessageContent
=
'test old message content'
;
$this
->
createMessageFile
(
$messageFileName
,
array
(
$oldMessage
=>
$oldMessageContent
));
$sourceFileContent
=
"Yii::t('
{
$category
}
', 'some new message')"
;
$this
->
createSourceFile
(
$sourceFileContent
);
$this
->
composeConfigFile
(
array
(
'languages'
=>
array
(
$language
),
'sourcePath'
=>
$this
->
sourcePath
,
'messagePath'
=>
$this
->
messagePath
,
'overwrite'
=>
true
,
'removeOld'
=>
false
,
));
$this
->
runMessageControllerAction
(
'generate'
,
array
(
$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
()
{
$language
=
'en'
;
$category
=
'test_category'
;
$messageFileName
=
$language
.
DIRECTORY_SEPARATOR
.
$category
.
'.php'
;
$zeroMessage
=
'test zero message'
;
$zeroMessageContent
=
'0'
;
$falseMessage
=
'test false message'
;
$falseMessageContent
=
'false'
;
$this
->
createMessageFile
(
$messageFileName
,
array
(
$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
(
array
(
'languages'
=>
array
(
$language
),
'sourcePath'
=>
$this
->
sourcePath
,
'messagePath'
=>
$this
->
messagePath
,
'overwrite'
=>
true
,
));
$this
->
runMessageControllerAction
(
'generate'
,
array
(
$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
()
{
$language
=
'en'
;
$category
=
'test_category'
;
$translators
=
array
(
'Yii::t'
,
'Custom::translate'
,
);
$sourceMessages
=
array
(
'first message'
,
'second message'
,
);
$sourceFileContent
=
''
;
foreach
(
$sourceMessages
as
$key
=>
$message
)
{
$sourceFileContent
.=
$translators
[
$key
]
.
"('
{
$category
}
', '
{
$message
}
');
\n
"
;
}
$this
->
createSourceFile
(
$sourceFileContent
);
$this
->
composeConfigFile
(
array
(
'languages'
=>
array
(
$language
),
'sourcePath'
=>
$this
->
sourcePath
,
'messagePath'
=>
$this
->
messagePath
,
'translator'
=>
$translators
,
));
$this
->
runMessageControllerAction
(
'generate'
,
array
(
$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
));
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment