1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
<?php
namespace Gettext\Extractors;
use Exception; use Gettext\Translations; use Gettext\Utils\StringReader;
/** * Class to get gettext strings from .mo files. */ class Mo extends Extractor implements ExtractorInterface { const MAGIC1 = -1794895138; const MAGIC2 = -569244523; const MAGIC3 = 2500072158;
/** * {@inheritdoc} */ public static function fromString($string, Translations $translations, array $options = []) { $stream = new StringReader($string); $magic = self::readInt($stream, 'V');
if (($magic === self::MAGIC1) || ($magic === self::MAGIC3)) { //to make sure it works for 64-bit platforms $byteOrder = 'V'; //low endian } elseif ($magic === (self::MAGIC2 & 0xFFFFFFFF)) { $byteOrder = 'N'; //big endian } else { throw new Exception('Not MO file'); }
self::readInt($stream, $byteOrder);
$total = self::readInt($stream, $byteOrder); //total string count $originals = self::readInt($stream, $byteOrder); //offset of original table $tran = self::readInt($stream, $byteOrder); //offset of translation table
$stream->seekto($originals); $table_originals = self::readIntArray($stream, $byteOrder, $total * 2);
$stream->seekto($tran); $table_translations = self::readIntArray($stream, $byteOrder, $total * 2);
for ($i = 0; $i < $total; ++$i) { $next = $i * 2;
$stream->seekto($table_originals[$next + 2]); $original = $stream->read($table_originals[$next + 1]);
$stream->seekto($table_translations[$next + 2]); $translated = $stream->read($table_translations[$next + 1]);
if ($original === '') { // Headers foreach (explode("\n", $translated) as $headerLine) { if ($headerLine === '') { continue; }
$headerChunks = preg_split('/:\s*/', $headerLine, 2); $translations->setHeader($headerChunks[0], isset($headerChunks[1]) ? $headerChunks[1] : ''); }
continue; }
$chunks = explode("\x04", $original, 2);
if (isset($chunks[1])) { $context = $chunks[0]; $original = $chunks[1]; } else { $context = ''; }
$chunks = explode("\x00", $original, 2);
if (isset($chunks[1])) { $original = $chunks[0]; $plural = $chunks[1]; } else { $plural = ''; }
$translation = $translations->insert($context, $original, $plural);
if ($translated === '') { continue; }
if ($plural === '') { $translation->setTranslation($translated); continue; }
$v = explode("\x00", $translated); $translation->setTranslation(array_shift($v)); $translation->setPluralTranslations($v); } }
/** * @param StringReader $stream * @param string $byteOrder */ private static function readInt(StringReader $stream, $byteOrder) { if (($read = $stream->read(4)) === false) { return false; }
$read = unpack($byteOrder, $read);
return array_shift($read); }
/** * @param StringReader $stream * @param string $byteOrder * @param int $count */ private static function readIntArray(StringReader $stream, $byteOrder, $count) { return unpack($byteOrder.$count, $stream->read(4 * $count)); } }
|