init($string); $magic = $this->readInt('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'); } $this->readInt($byteOrder); $total = $this->readInt($byteOrder); //total string count $originals = $this->readInt($byteOrder); //offset of original table $tran = $this->readInt($byteOrder); //offset of translation table $this->seekto($originals); $table_originals = $this->readIntArray($byteOrder, $total * 2); $this->seekto($tran); $table_translations = $this->readIntArray($byteOrder, $total * 2); for ($i = 0; $i < $total; ++$i) { $next = $i * 2; $this->seekto($table_originals[$next + 2]); $original = $this->read($table_originals[$next + 1]); $this->seekto($table_translations[$next + 2]); $translated = $this->read($table_translations[$next + 1]); // Headers if ($original === '') { foreach (explode("\n", $translated) as $headerLine) { if ($headerLine === '') { continue; } $headerChunks = preg_split('/:\s*/', $headerLine, 2); $translations->getHeaders()->set($headerChunks[0], isset($headerChunks[1]) ? $headerChunks[1] : ''); } continue; } $context = $plural = null; $chunks = explode("\x04", $original, 2); if (isset($chunks[1])) { list($context, $original) = $chunks; } $chunks = explode("\x00", $original, 2); if (isset($chunks[1])) { list($original, $plural) = $chunks; } $translation = $this->createTranslation($context, $original, $plural); $translations->add($translation); if ($translated === '') { continue; } if ($plural === null) { $translation->translate($translated); continue; } $v = explode("\x00", $translated); $translation->translate(array_shift($v)); $translation->translatePlural(...array_filter($v)); } return $translations; } private function init(string $string): void { $this->string = $string; $this->position = 0; $this->length = strlen($string); } private function read(int $bytes): string { $data = substr($this->string, $this->position, $bytes); $this->seekTo($this->position + $bytes); return $data; } private function seekTo(int $position): void { $this->position = ($this->length < $position) ? $this->length : $position; } private function readInt(string $byteOrder): int { if (($read = $this->read(4)) === false) { return 0; } $read = (array) unpack($byteOrder, $read); return (int) array_shift($read); } private function readIntArray(string $byteOrder, int $count): array { return unpack($byteOrder.$count, $this->read(4 * $count)) ?: []; } }