Skip to content

Commit 56cb7a5

Browse files
committed
[Translation] add gettext PO and MO Dumper
1 parent 2d53751 commit 56cb7a5

File tree

5 files changed

+204
-0
lines changed

5 files changed

+204
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
<parameter key="translation.loader.ini.class">Symfony\Component\Translation\Loader\IniFileLoader</parameter>
2020
<parameter key="translation.dumper.php.class">Symfony\Component\Translation\Dumper\PhpFileDumper</parameter>
2121
<parameter key="translation.dumper.xliff.class">Symfony\Component\Translation\Dumper\XliffFileDumper</parameter>
22+
<parameter key="translation.dumper.po.class">Symfony\Component\Translation\Dumper\PoFileDumper</parameter>
23+
<parameter key="translation.dumper.mo.class">Symfony\Component\Translation\Dumper\MoFileDumper</parameter>
2224
<parameter key="translation.dumper.yml.class">Symfony\Component\Translation\Dumper\YamlFileDumper</parameter>
2325
<parameter key="translation.dumper.qt.class">Symfony\Component\Translation\Dumper\QtFileDumper</parameter>
2426
<parameter key="translation.dumper.csv.class">Symfony\Component\Translation\Dumper\CsvFileDumper</parameter>
@@ -90,6 +92,14 @@
9092
<tag name="translation.dumper" alias="xliff" />
9193
</service>
9294

95+
<service id="translation.dumper.po" class="%translation.dumper.po.class%">
96+
<tag name="translation.dumper" alias="po" />
97+
</service>
98+
99+
<service id="translation.dumper.mo" class="%translation.dumper.mo.class%">
100+
<tag name="translation.dumper" alias="mo" />
101+
</service>
102+
93103
<service id="translation.dumper.yml" class="%translation.dumper.yml.class%">
94104
<tag name="translation.dumper" alias="yml" />
95105
</service>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
namespace Symfony\Component\Translation\Dumper;
13+
14+
use Symfony\Component\Translation\MessageCatalogue;
15+
use Symfony\Component\Translation\Loader\MoFileLoader;
16+
17+
/**
18+
* MoFileDumper generates a gettext formated string representation of a message catalogue.
19+
*
20+
* @author Stealth35
21+
*/
22+
class MoFileDumper extends FileDumper
23+
{
24+
/**
25+
* {@inheritDoc}
26+
*/
27+
public function format(MessageCatalogue $messages, $domain = 'messages')
28+
{
29+
$output = $sources = $targets = $source_offsets = $target_offsets = '';
30+
$offsets = array();
31+
$size = 0;
32+
33+
foreach ($messages->all($domain) as $source => $target) {
34+
$offsets[] = array_map('strlen', array($sources, $source, $targets, $target));
35+
$sources .= "\0".$source;
36+
$targets .= "\0".$target;
37+
++$size;
38+
}
39+
40+
$header = array(
41+
'magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC,
42+
'formatRevision' => 0,
43+
'count' => $size,
44+
'offsetId' => MoFileLoader::MO_HEADER_SIZE,
45+
'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size),
46+
'sizeHashes' => 0,
47+
'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size),
48+
);
49+
50+
$sources_size = strlen($sources);
51+
$sources_start = $header['offsetHashes'] + 1;
52+
53+
foreach ($offsets as $offset) {
54+
$source_offsets .= $this->writeLong($offset[1])
55+
. $this->writeLong($offset[0] + $sources_start);
56+
$target_offsets .= $this->writeLong($offset[3])
57+
. $this->writeLong($offset[2] + $sources_start + $sources_size);
58+
}
59+
60+
$output = implode(array_map(array($this, 'writeLong'), $header))
61+
. $source_offsets
62+
. $target_offsets
63+
. $sources
64+
. $targets
65+
;
66+
67+
return $output;
68+
}
69+
70+
/**
71+
* {@inheritDoc}
72+
*/
73+
protected function getExtension()
74+
{
75+
return 'mo';
76+
}
77+
78+
private function writeLong($str)
79+
{
80+
return pack('V*', $str);
81+
}
82+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
namespace Symfony\Component\Translation\Dumper;
13+
14+
use Symfony\Component\Translation\MessageCatalogue;
15+
16+
/**
17+
* PoFileDumper generates a gettext formated string representation of a message catalogue.
18+
*
19+
* @author Stealth35
20+
*/
21+
class PoFileDumper extends FileDumper
22+
{
23+
/**
24+
* {@inheritDoc}
25+
*/
26+
public function format(MessageCatalogue $messages, $domain = 'messages')
27+
{
28+
$output = '';
29+
30+
foreach ($messages->all($domain) as $source => $target) {
31+
$output .= sprintf("msgid \"%s\"\n", $this->escape($source));
32+
$output .= sprintf("msgstr \"%s\"\n\n", $this->escape($target));
33+
}
34+
35+
return $output;
36+
}
37+
38+
/**
39+
* {@inheritDoc}
40+
*/
41+
protected function getExtension()
42+
{
43+
return 'po';
44+
}
45+
46+
private function escape($str)
47+
{
48+
return addcslashes($str, "\0..\37\42\134");
49+
}
50+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
namespace Symfony\Tests\Component\Translation\Dumper;
13+
14+
use Symfony\Component\Translation\MessageCatalogue;
15+
use Symfony\Component\Translation\Dumper\MoFileDumper;
16+
17+
class MoFileDumperTest extends \PHPUnit_Framework_TestCase
18+
{
19+
public function testDump()
20+
{
21+
$catalogue = new MessageCatalogue('en');
22+
$catalogue->add(array('foo' => 'bar'));
23+
24+
$tempDir = sys_get_temp_dir();
25+
$dumper = new MoFileDumper();
26+
$dumperString = $dumper->dump($catalogue, array('path' => $tempDir));
27+
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.mo'), file_get_contents($tempDir.'/messages.en.mo'));
28+
29+
unlink($tempDir.'/messages.en.mo');
30+
}
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
namespace Symfony\Tests\Component\Translation\Dumper;
13+
14+
use Symfony\Component\Translation\MessageCatalogue;
15+
use Symfony\Component\Translation\Dumper\PoFileDumper;
16+
17+
class PoFileDumperTest extends \PHPUnit_Framework_TestCase
18+
{
19+
public function testDump()
20+
{
21+
$catalogue = new MessageCatalogue('en');
22+
$catalogue->add(array('foo' => 'bar'));
23+
24+
$tempDir = sys_get_temp_dir();
25+
$dumper = new PoFileDumper();
26+
$dumperString = $dumper->dump($catalogue, array('path' => $tempDir));
27+
$this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.po'), file_get_contents($tempDir.'/messages.en.po'));
28+
29+
unlink($tempDir.'/messages.en.po');
30+
}
31+
}

0 commit comments

Comments
 (0)