1 | <?php
|
---|
2 | /**
|
---|
3 | * Class for working with MO files
|
---|
4 | *
|
---|
5 | * @version $Id: mo.php 106 2009-04-23 19:48:22Z nbachiyski $
|
---|
6 | * @package pomo
|
---|
7 | * @subpackage mo
|
---|
8 | */
|
---|
9 |
|
---|
10 | require_once dirname(__FILE__) . '/translations.php';
|
---|
11 | require_once dirname(__FILE__) . '/streams.php';
|
---|
12 |
|
---|
13 | class MO extends Gettext_Translations {
|
---|
14 |
|
---|
15 | var $_nplurals = 2;
|
---|
16 |
|
---|
17 | /**
|
---|
18 | * Fills up with the entries from MO file $filename
|
---|
19 | *
|
---|
20 | * @param string $filename MO file to load
|
---|
21 | */
|
---|
22 | function import_from_file($filename) {
|
---|
23 | $reader = new POMO_CachedIntFileReader($filename);
|
---|
24 | if (isset($reader->error)) {
|
---|
25 | return false;
|
---|
26 | }
|
---|
27 | return $this->import_from_reader($reader);
|
---|
28 | }
|
---|
29 |
|
---|
30 | function export_to_file($filename) {
|
---|
31 | $fh = fopen($filename, 'wb');
|
---|
32 | if ( !$fh ) return false;
|
---|
33 | $entries = array_filter($this->entries, create_function('$e', 'return !empty($e->translations);'));
|
---|
34 | ksort($entries);
|
---|
35 | $magic = 0x950412de;
|
---|
36 | $revision = 0;
|
---|
37 | $total = count($entries) + 1; // all the headers are one entry
|
---|
38 | $originals_lenghts_addr = 28;
|
---|
39 | $translations_lenghts_addr = $originals_lenghts_addr + 8 * $total;
|
---|
40 | $size_of_hash = 0;
|
---|
41 | $hash_addr = $translations_lenghts_addr + 8 * $total;
|
---|
42 | $current_addr = $hash_addr;
|
---|
43 | fwrite($fh, pack('V*', $magic, $revision, $total, $originals_lenghts_addr,
|
---|
44 | $translations_lenghts_addr, $size_of_hash, $hash_addr));
|
---|
45 | fseek($fh, $originals_lenghts_addr);
|
---|
46 |
|
---|
47 | // headers' msgid is an empty string
|
---|
48 | fwrite($fh, pack('VV', 0, $current_addr));
|
---|
49 | $current_addr++;
|
---|
50 | $originals_table = chr(0);
|
---|
51 |
|
---|
52 | foreach($entries as $entry) {
|
---|
53 | $originals_table .= $this->export_original($entry) . chr(0);
|
---|
54 | $length = strlen($this->export_original($entry));
|
---|
55 | fwrite($fh, pack('VV', $length, $current_addr));
|
---|
56 | $current_addr += $length + 1; // account for the NULL byte after
|
---|
57 | }
|
---|
58 |
|
---|
59 | $exported_headers = $this->export_headers();
|
---|
60 | fwrite($fh, pack('VV', strlen($exported_headers), $current_addr));
|
---|
61 | $current_addr += strlen($exported_headers) + 1;
|
---|
62 | $translations_table = $exported_headers . chr(0);
|
---|
63 |
|
---|
64 | foreach($entries as $entry) {
|
---|
65 | $translations_table .= $this->export_translations($entry) . chr(0);
|
---|
66 | $length = strlen($this->export_translations($entry));
|
---|
67 | fwrite($fh, pack('VV', $length, $current_addr));
|
---|
68 | $current_addr += $length + 1;
|
---|
69 | }
|
---|
70 |
|
---|
71 | fwrite($fh, $originals_table);
|
---|
72 | fwrite($fh, $translations_table);
|
---|
73 | fclose($fh);
|
---|
74 | }
|
---|
75 |
|
---|
76 | function export_original($entry) {
|
---|
77 | //TODO: warnings for control characters
|
---|
78 | $exported = $entry->singular;
|
---|
79 | if ($entry->is_plural) $exported .= chr(0).$entry->plural;
|
---|
80 | if (!is_null($entry->context)) $exported = $entry->context . chr(4) . $exported;
|
---|
81 | return $exported;
|
---|
82 | }
|
---|
83 |
|
---|
84 | function export_translations($entry) {
|
---|
85 | //TODO: warnings for control characters
|
---|
86 | return implode(chr(0), $entry->translations);
|
---|
87 | }
|
---|
88 |
|
---|
89 | function export_headers() {
|
---|
90 | $exported = '';
|
---|
91 | foreach($this->headers as $header => $value) {
|
---|
92 | $exported.= "$header: $value\n";
|
---|
93 | }
|
---|
94 | return $exported;
|
---|
95 | }
|
---|
96 |
|
---|
97 | function get_byteorder($magic) {
|
---|
98 |
|
---|
99 | // The magic is 0x950412de
|
---|
100 |
|
---|
101 | // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565
|
---|
102 | $magic_little = (int) - 1794895138;
|
---|
103 | $magic_little_64 = (int) 2500072158;
|
---|
104 | // 0xde120495
|
---|
105 | $magic_big = ((int) - 569244523) && 0xFFFFFFFF;
|
---|
106 |
|
---|
107 | if ($magic_little == $magic || $magic_little_64 == $magic) {
|
---|
108 | return 'little';
|
---|
109 | } else if ($magic_big == $magic) {
|
---|
110 | return 'big';
|
---|
111 | } else {
|
---|
112 | return false;
|
---|
113 | }
|
---|
114 | }
|
---|
115 |
|
---|
116 | function import_from_reader($reader) {
|
---|
117 | $reader->setEndian('little');
|
---|
118 | $endian = MO::get_byteorder($reader->readint32());
|
---|
119 | if (false === $endian) {
|
---|
120 | return false;
|
---|
121 | }
|
---|
122 | $reader->setEndian($endian);
|
---|
123 |
|
---|
124 | $revision = $reader->readint32();
|
---|
125 | $total = $reader->readint32();
|
---|
126 | // get addresses of array of lenghts and offsets for original string and translations
|
---|
127 | $originals_lenghts_addr = $reader->readint32();
|
---|
128 | $translations_lenghts_addr = $reader->readint32();
|
---|
129 |
|
---|
130 | $reader->seekto($originals_lenghts_addr);
|
---|
131 | $originals_lenghts = $reader->readint32array($total * 2); // each of
|
---|
132 | $reader->seekto($translations_lenghts_addr);
|
---|
133 | $translations_lenghts = $reader->readint32array($total * 2);
|
---|
134 |
|
---|
135 | $length = create_function('$i', 'return $i * 2 + 1;');
|
---|
136 | $offset = create_function('$i', 'return $i * 2 + 2;');
|
---|
137 |
|
---|
138 | for ($i = 0; $i < $total; ++$i) {
|
---|
139 | $reader->seekto($originals_lenghts[$offset($i)]);
|
---|
140 | $original = $reader->read($originals_lenghts[$length($i)]);
|
---|
141 | $reader->seekto($translations_lenghts[$offset($i)]);
|
---|
142 | $translation = $reader->read($translations_lenghts[$length($i)]);
|
---|
143 | if ('' == $original) {
|
---|
144 | $this->set_headers($this->make_headers($translation));
|
---|
145 | } else {
|
---|
146 | $this->add_entry($this->make_entry($original, $translation));
|
---|
147 | }
|
---|
148 | }
|
---|
149 | return true;
|
---|
150 | }
|
---|
151 |
|
---|
152 | /**
|
---|
153 | * @static
|
---|
154 | */
|
---|
155 | function &make_entry($original, $translation) {
|
---|
156 | $args = array();
|
---|
157 | // look for context
|
---|
158 | $parts = explode(chr(4), $original);
|
---|
159 | if (isset($parts[1])) {
|
---|
160 | $original = $parts[1];
|
---|
161 | $args['context'] = $parts[0];
|
---|
162 | }
|
---|
163 | // look for plural original
|
---|
164 | $parts = explode(chr(0), $original);
|
---|
165 | $args['singular'] = $parts[0];
|
---|
166 | if (isset($parts[1])) {
|
---|
167 | $args['plural'] = $parts[1];
|
---|
168 | }
|
---|
169 | // plural translations are also separated by \0
|
---|
170 | $args['translations'] = explode(chr(0), $translation);
|
---|
171 | $entry = & new Translation_Entry($args);
|
---|
172 | return $entry;
|
---|
173 | }
|
---|
174 |
|
---|
175 | function select_plural_form($count) {
|
---|
176 | return $this->gettext_select_plural_form($count);
|
---|
177 | }
|
---|
178 |
|
---|
179 | function get_plural_forms_count() {
|
---|
180 | return $this->_nplurals;
|
---|
181 | }
|
---|
182 |
|
---|
183 |
|
---|
184 | }
|
---|
185 | ?>
|
---|