Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Translation\Loader; Chris@0: Chris@0: /** Chris@0: * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) Chris@0: * @copyright Copyright (c) 2012, Clemens Tolboom Chris@0: */ Chris@0: class PoFileLoader extends FileLoader Chris@0: { Chris@0: /** Chris@0: * Parses portable object (PO) format. Chris@0: * Chris@0: * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files Chris@0: * we should be able to parse files having: Chris@0: * Chris@0: * white-space Chris@0: * # translator-comments Chris@0: * #. extracted-comments Chris@0: * #: reference... Chris@0: * #, flag... Chris@0: * #| msgid previous-untranslated-string Chris@0: * msgid untranslated-string Chris@0: * msgstr translated-string Chris@0: * Chris@0: * extra or different lines are: Chris@0: * Chris@0: * #| msgctxt previous-context Chris@0: * #| msgid previous-untranslated-string Chris@0: * msgctxt context Chris@0: * Chris@0: * #| msgid previous-untranslated-string-singular Chris@0: * #| msgid_plural previous-untranslated-string-plural Chris@0: * msgid untranslated-string-singular Chris@0: * msgid_plural untranslated-string-plural Chris@0: * msgstr[0] translated-string-case-0 Chris@0: * ... Chris@0: * msgstr[N] translated-string-case-n Chris@0: * Chris@0: * The definition states: Chris@0: * - white-space and comments are optional. Chris@0: * - msgid "" that an empty singleline defines a header. Chris@0: * Chris@0: * This parser sacrifices some features of the reference implementation the Chris@0: * differences to that implementation are as follows. Chris@0: * - No support for comments spanning multiple lines. Chris@0: * - Translator and extracted comments are treated as being the same type. Chris@0: * - Message IDs are allowed to have other encodings as just US-ASCII. Chris@0: * Chris@0: * Items with an empty id are ignored. Chris@0: * Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function loadResource($resource) Chris@0: { Chris@0: $stream = fopen($resource, 'r'); Chris@0: Chris@17: $defaults = [ Chris@17: 'ids' => [], Chris@0: 'translated' => null, Chris@17: ]; Chris@0: Chris@17: $messages = []; Chris@0: $item = $defaults; Chris@17: $flags = []; Chris@0: Chris@0: while ($line = fgets($stream)) { Chris@0: $line = trim($line); Chris@0: Chris@14: if ('' === $line) { Chris@0: // Whitespace indicated current item is done Chris@17: if (!\in_array('fuzzy', $flags)) { Chris@0: $this->addMessage($messages, $item); Chris@0: } Chris@0: $item = $defaults; Chris@17: $flags = []; Chris@14: } elseif ('#,' === substr($line, 0, 2)) { Chris@0: $flags = array_map('trim', explode(',', substr($line, 2))); Chris@14: } elseif ('msgid "' === substr($line, 0, 7)) { Chris@0: // We start a new msg so save previous Chris@0: // TODO: this fails when comments or contexts are added Chris@0: $this->addMessage($messages, $item); Chris@0: $item = $defaults; Chris@0: $item['ids']['singular'] = substr($line, 7, -1); Chris@14: } elseif ('msgstr "' === substr($line, 0, 8)) { Chris@0: $item['translated'] = substr($line, 8, -1); Chris@14: } elseif ('"' === $line[0]) { Chris@0: $continues = isset($item['translated']) ? 'translated' : 'ids'; Chris@0: Chris@17: if (\is_array($item[$continues])) { Chris@0: end($item[$continues]); Chris@0: $item[$continues][key($item[$continues])] .= substr($line, 1, -1); Chris@0: } else { Chris@0: $item[$continues] .= substr($line, 1, -1); Chris@0: } Chris@14: } elseif ('msgid_plural "' === substr($line, 0, 14)) { Chris@0: $item['ids']['plural'] = substr($line, 14, -1); Chris@14: } elseif ('msgstr[' === substr($line, 0, 7)) { Chris@0: $size = strpos($line, ']'); Chris@0: $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1); Chris@0: } Chris@0: } Chris@0: // save last item Chris@17: if (!\in_array('fuzzy', $flags)) { Chris@0: $this->addMessage($messages, $item); Chris@0: } Chris@0: fclose($stream); Chris@0: Chris@0: return $messages; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Save a translation item to the messages. Chris@0: * Chris@0: * A .po file could contain by error missing plural indexes. We need to Chris@0: * fix these before saving them. Chris@0: */ Chris@0: private function addMessage(array &$messages, array $item) Chris@0: { Chris@17: if (\is_array($item['translated'])) { Chris@0: $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]); Chris@0: if (isset($item['ids']['plural'])) { Chris@0: $plurals = $item['translated']; Chris@0: // PO are by definition indexed so sort by index. Chris@0: ksort($plurals); Chris@0: // Make sure every index is filled. Chris@0: end($plurals); Chris@0: $count = key($plurals); Chris@0: // Fill missing spots with '-'. Chris@0: $empties = array_fill(0, $count + 1, '-'); Chris@0: $plurals += $empties; Chris@0: ksort($plurals); Chris@0: $messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals)); Chris@0: } Chris@0: } elseif (!empty($item['ids']['singular'])) { Chris@0: $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']); Chris@0: } Chris@0: } Chris@0: }