Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6ac56d3

Browse files
committed
Adding IntlMessageConverter
1 parent5b08019 commit6ac56d3

File tree

4 files changed

+298
-0
lines changed

4 files changed

+298
-0
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@
113113
<tagname="console.command"command="translation:update" />
114114
</service>
115115

116+
<serviceid="console.command.translation_icu_convert"class="Symfony\Component\Translation\Command\IntlConvertCommand">
117+
<argumenttype="service"id="translation.writer" />
118+
<argumenttype="service"id="translation.reader" />
119+
<tagname="console.command"command="translation:convert-to-icu-messages" />
120+
</service>
121+
122+
116123
<serviceid="console.command.workflow_dump"class="Symfony\Bundle\FrameworkBundle\Command\WorkflowDumpCommand">
117124
<tagname="console.command"command="workflow:dump" />
118125
</service>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Translation\Command;
13+
14+
useSymfony\Component\Console\Command\Command;
15+
useSymfony\Component\Console\Input\InputArgument;
16+
useSymfony\Component\Console\Input\InputInterface;
17+
useSymfony\Component\Console\Input\InputOption;
18+
useSymfony\Component\Console\Output\OutputInterface;
19+
useSymfony\Component\Console\Style\SymfonyStyle;
20+
useSymfony\Component\HttpKernel\KernelInterface;
21+
useSymfony\Component\Translation\MessageCatalogue;
22+
useSymfony\Component\Translation\Reader\TranslationReaderInterface;
23+
useSymfony\Component\Translation\Util\IntlMessageConverter;
24+
useSymfony\Component\Translation\Writer\TranslationWriterInterface;
25+
26+
/**
27+
* Convert to Intl styled message format.
28+
*
29+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
30+
*/
31+
class IntlConvertCommandextends Command
32+
{
33+
protectedstatic$defaultName ='translation:convert-to-icu-messages';
34+
35+
private$writer;
36+
private$reader;
37+
38+
publicfunction__construct(TranslationWriterInterface$writer,TranslationReaderInterface$reader)
39+
{
40+
parent::__construct();
41+
42+
$this->writer =$writer;
43+
$this->reader =$reader;
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
protectedfunctionconfigure()
50+
{
51+
$this
52+
->setDescription('Convert from Symfony 3 plural format to ICU message format.')
53+
->addArgument('locale', InputArgument::REQUIRED,'The locale')
54+
->addArgument('path',null,'A file or a directory')
55+
->addOption('domain',null, InputOption::VALUE_OPTIONAL,'The messages domain')
56+
->addOption('output-format',null, InputOption::VALUE_OPTIONAL,'Override the default output format','xlf')
57+
;
58+
}
59+
60+
protectedfunctionexecute(InputInterface$input,OutputInterface$output)
61+
{
62+
$io =newSymfonyStyle($input,$output);
63+
$path =$input->getArgument('path');
64+
$locale =$input->getArgument('locale');
65+
$domain =$input->getOption('domain');
66+
/** @var KernelInterface $kernel */
67+
$kernel =$this->getApplication()->getKernel();
68+
69+
// Define Root Paths
70+
$transPaths =$kernel->getProjectDir().'/translations';
71+
if (null !==$path) {
72+
$transPaths =$path;
73+
}
74+
75+
// load any existing messages from the translation files
76+
$currentCatalogue =newMessageCatalogue($locale);
77+
if (!is_dir($transPaths)) {
78+
thrownew \LogicException('The "path" must be a directory.');
79+
}
80+
$this->reader->read($transPaths,$currentCatalogue);
81+
82+
$allMessages =$currentCatalogue->all($domain);
83+
if (null !==$domain) {
84+
$allMessages =array($domain =>$allMessages);
85+
}
86+
87+
$updated =array();
88+
foreach ($allMessagesas$messageDomain =>$messages) {
89+
foreach ($messagesas$key =>$message) {
90+
$updated[$messageDomain][$key] = IntlMessageConverter::convert($message);
91+
}
92+
}
93+
94+
$this->writer->write(newMessageCatalogue($locale,$updated),$input->getOption('output-format'),array('path' =>$transPaths));
95+
}
96+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespaceSymfony\Component\Translation\Tests\Util;
6+
7+
usePHPUnit\Framework\TestCase;
8+
useSymfony\Component\Translation\Util\IntlMessageConverter;
9+
10+
class IntlMessageConverterTestextends TestCase
11+
{
12+
/**
13+
* @dataProvider getTestData
14+
*/
15+
publicfunctiontestConvert($input,$output)
16+
{
17+
$result = IntlMessageConverter::convert($input);
18+
$this->assertEquals($output,$result);
19+
}
20+
21+
/**
22+
* We cannot use negative Inf together with positive Inf.
23+
*/
24+
publicfunctiontestImpossibleConvert()
25+
{
26+
$this->expectException(\LogicException::class);
27+
IntlMessageConverter::convert(']-Inf, -2[ Negative|]1,Inf[ Positive');
28+
}
29+
30+
publicfunctiongetTestData()
31+
{
32+
yieldarray(
33+
'{0} There are no apples|{1} There is one apple|]1,Inf[ There %name% are %count% apples',
34+
<<<ICU
35+
{ COUNT, plural,
36+
=0 {There are no apples}
37+
=1 {There is one apple}
38+
other {There {name} are # apples}
39+
}
40+
ICU
41+
);
42+
yieldarray('foo','foo');
43+
yieldarray('Hello %username%','Hello {username}');
44+
45+
yieldarray(
46+
']-7, -2] Negative|[2, 7] Small|]10,Inf[ Many',
47+
<<<ICU
48+
{ COUNT, plural,
49+
=-6 {Negative}
50+
=-5 {Negative}
51+
=-4 {Negative}
52+
=-3 {Negative}
53+
=-2 {Negative}
54+
=2 {Small}
55+
=3 {Small}
56+
=4 {Small}
57+
=5 {Small}
58+
=6 {Small}
59+
=7 {Small}
60+
other {Many}
61+
}
62+
ICU
63+
);
64+
65+
// Test overlapping, make sure we have the same behaviour as Symfony
66+
yieldarray(
67+
'[2,5] Small|]3,Inf[ Many',
68+
<<<ICU
69+
{ COUNT, plural,
70+
=2 {Small}
71+
=3 {Small}
72+
=4 {Small}
73+
=5 {Small}
74+
other {Many}
75+
}
76+
ICU
77+
);
78+
}
79+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Translation\Util;
13+
14+
/**
15+
* Convert from Symfony 3's plural syntax to Intl message format.
16+
* {@link https://messageformat.github.io/messageformat/page-guide}.
17+
*
18+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
19+
*/
20+
class IntlMessageConverter
21+
{
22+
publicstaticfunctionconvert(string$message):string
23+
{
24+
$array =self::getMessageArray($message);
25+
26+
if (1 ===\count($array) &&isset($array[0])) {
27+
returnpreg_replace('|%(.*?)%|s','{$1}',$message);
28+
}
29+
30+
$icu =self::buildIcuString($array);
31+
32+
return$icu;
33+
}
34+
35+
/**
36+
* Get an ICU like array.
37+
*/
38+
privatestaticfunctiongetMessageArray(string$message):array
39+
{
40+
$parts =array();
41+
if (preg_match('/^\|++$/',$message)) {
42+
$parts =explode('|',$message);
43+
}elseif (preg_match_all('/(?:\|\||[^\|])++/',$message,$matches)) {
44+
$parts =$matches[0];
45+
}
46+
47+
$intervalRegexp = <<<'EOF'
48+
/^(?P<interval>
49+
({\s*
50+
(\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
51+
\s*})
52+
53+
|
54+
55+
(?P<left_delimiter>[\[\]])
56+
\s*
57+
(?P<left>-Inf|\-?\d+(\.\d+)?)
58+
\s*,\s*
59+
(?P<right>\+?Inf|\-?\d+(\.\d+)?)
60+
\s*
61+
(?P<right_delimiter>[\[\]])
62+
)\s*(?P<message>.*?)$/xs
63+
EOF;
64+
65+
$standardRules =array();
66+
foreach ($partsas$part) {
67+
$part =trim(str_replace('||','|',$part));
68+
69+
// try to match an explicit rule, then fallback to the standard ones
70+
if (preg_match($intervalRegexp,$part,$matches)) {
71+
if ($matches[2]) {
72+
foreach (explode(',',$matches[3])as$n) {
73+
$standardRules['='.$n] =$matches['message'];
74+
}
75+
}else {
76+
$leftNumber ='-Inf' ===$matches['left'] ? -INF : (float)$matches['left'];
77+
$rightNumber =\is_numeric($matches['right']) ? (float)$matches['right'] :INF;
78+
79+
$leftNumber = ('[' ===$matches['left_delimiter'] ?$leftNumber :1 +$leftNumber);
80+
$rightNumber = (']' ===$matches['right_delimiter'] ?1 +$rightNumber :$rightNumber);
81+
82+
if ($leftNumber !== -INF &&INF !==$rightNumber) {
83+
for ($i =$leftNumber;$i <$rightNumber; ++$i) {
84+
$standardRules['='.$i] =$matches['message'];
85+
}
86+
}else {
87+
// $rightNumber is INF or $leftNumber is -INF
88+
if (isset($standardRules['other'])) {
89+
thrownew \LogicException(sprintf('%s does not support converting messages with both "-Inf" and "Inf". Message: "%s"',__CLASS__,$message));
90+
}
91+
$standardRules['other'] =$matches['message'];
92+
}
93+
}
94+
}elseif (preg_match('/^\w+\:\s*(.*?)$/',$part,$matches)) {
95+
$standardRules[] =$matches[1];
96+
}else {
97+
$standardRules[] =$part;
98+
}
99+
}
100+
101+
return$standardRules;
102+
}
103+
104+
privatestaticfunctionbuildIcuString(array$data):string
105+
{
106+
$icu ="{ COUNT, plural,\n";
107+
foreach ($dataas$key =>$message) {
108+
$message =strtr($message,array('%count%' =>'#'));
109+
$message =preg_replace('|%(.*?)%|s','{$1}',$message);
110+
$icu .=sprintf(" %s {%s}\n",$key,$message);
111+
}
112+
$icu .='}';
113+
114+
return$icu;
115+
}
116+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp