endian = 'V'; $this->str = $var; $this->len = strlen($var); $magic = $this->readInt() & 0xffffffff; if ($magic === 0x950412de) { // Low endian. $this->endian = 'V'; } elseif ($magic === 0xde120495) { // Big endian. $this->endian = 'N'; } else { throw new \RuntimeException('Not a Gettext file (.mo).'); } // Skip revision number. $rev = $this->readInt(); // Total count. $total = $this->readInt(); // Offset of original table. $originals = $this->readInt(); // Offset of translation table. $translations = $this->readInt(); // Each table consists of string length and offset of the string. $this->seek($originals); $table_originals = $this->readIntArray($total * 2); $this->seek($translations); $table_translations = $this->readIntArray($total * 2); $items = []; for ($i = 0; $i < $total; $i++) { $this->seek($table_originals[$i * 2 + 2]); // TODO: Original string can have context concatenated on it. We do not yet support that. $original = $this->read($table_originals[$i * 2 + 1]); if ($original) { $this->seek($table_translations[$i * 2 + 2]); // TODO: Plural forms are stored by letting the plural of the original string follow the singular of the original string, separated through a NUL byte. $translated = $this->read($table_translations[$i * 2 + 1]); $items[$original] = $translated; } } return $items; } /** * @return int */ protected function readInt() { $read = $this->read(4); if ($read === false) { return false; } $read = unpack($this->endian, $read); return array_shift($read); } /** * @param $count * @return array */ protected function readIntArray($count) { return unpack($this->endian . $count, $this->read(4 * $count)); } /** * @param $bytes * @return string */ private function read($bytes) { $data = substr($this->str, $this->pos, $bytes); $this->seek($this->pos + $bytes); return $data; } /** * @param $pos * @return mixed */ private function seek($pos) { $this->pos = $pos < $this->len ? $pos : $this->len; return $this->pos; } }