[44] | 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 | ?>
|
---|