Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\Translation\Loader;
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
|
Chris@0
|
16 * @copyright Copyright (c) 2012, Clemens Tolboom
|
Chris@0
|
17 */
|
Chris@0
|
18 class PoFileLoader extends FileLoader
|
Chris@0
|
19 {
|
Chris@0
|
20 /**
|
Chris@0
|
21 * Parses portable object (PO) format.
|
Chris@0
|
22 *
|
Chris@0
|
23 * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
|
Chris@0
|
24 * we should be able to parse files having:
|
Chris@0
|
25 *
|
Chris@0
|
26 * white-space
|
Chris@0
|
27 * # translator-comments
|
Chris@0
|
28 * #. extracted-comments
|
Chris@0
|
29 * #: reference...
|
Chris@0
|
30 * #, flag...
|
Chris@0
|
31 * #| msgid previous-untranslated-string
|
Chris@0
|
32 * msgid untranslated-string
|
Chris@0
|
33 * msgstr translated-string
|
Chris@0
|
34 *
|
Chris@0
|
35 * extra or different lines are:
|
Chris@0
|
36 *
|
Chris@0
|
37 * #| msgctxt previous-context
|
Chris@0
|
38 * #| msgid previous-untranslated-string
|
Chris@0
|
39 * msgctxt context
|
Chris@0
|
40 *
|
Chris@0
|
41 * #| msgid previous-untranslated-string-singular
|
Chris@0
|
42 * #| msgid_plural previous-untranslated-string-plural
|
Chris@0
|
43 * msgid untranslated-string-singular
|
Chris@0
|
44 * msgid_plural untranslated-string-plural
|
Chris@0
|
45 * msgstr[0] translated-string-case-0
|
Chris@0
|
46 * ...
|
Chris@0
|
47 * msgstr[N] translated-string-case-n
|
Chris@0
|
48 *
|
Chris@0
|
49 * The definition states:
|
Chris@0
|
50 * - white-space and comments are optional.
|
Chris@0
|
51 * - msgid "" that an empty singleline defines a header.
|
Chris@0
|
52 *
|
Chris@0
|
53 * This parser sacrifices some features of the reference implementation the
|
Chris@0
|
54 * differences to that implementation are as follows.
|
Chris@0
|
55 * - No support for comments spanning multiple lines.
|
Chris@0
|
56 * - Translator and extracted comments are treated as being the same type.
|
Chris@0
|
57 * - Message IDs are allowed to have other encodings as just US-ASCII.
|
Chris@0
|
58 *
|
Chris@0
|
59 * Items with an empty id are ignored.
|
Chris@0
|
60 *
|
Chris@0
|
61 * {@inheritdoc}
|
Chris@0
|
62 */
|
Chris@0
|
63 protected function loadResource($resource)
|
Chris@0
|
64 {
|
Chris@0
|
65 $stream = fopen($resource, 'r');
|
Chris@0
|
66
|
Chris@17
|
67 $defaults = [
|
Chris@17
|
68 'ids' => [],
|
Chris@0
|
69 'translated' => null,
|
Chris@17
|
70 ];
|
Chris@0
|
71
|
Chris@17
|
72 $messages = [];
|
Chris@0
|
73 $item = $defaults;
|
Chris@17
|
74 $flags = [];
|
Chris@0
|
75
|
Chris@0
|
76 while ($line = fgets($stream)) {
|
Chris@0
|
77 $line = trim($line);
|
Chris@0
|
78
|
Chris@14
|
79 if ('' === $line) {
|
Chris@0
|
80 // Whitespace indicated current item is done
|
Chris@17
|
81 if (!\in_array('fuzzy', $flags)) {
|
Chris@0
|
82 $this->addMessage($messages, $item);
|
Chris@0
|
83 }
|
Chris@0
|
84 $item = $defaults;
|
Chris@17
|
85 $flags = [];
|
Chris@14
|
86 } elseif ('#,' === substr($line, 0, 2)) {
|
Chris@0
|
87 $flags = array_map('trim', explode(',', substr($line, 2)));
|
Chris@14
|
88 } elseif ('msgid "' === substr($line, 0, 7)) {
|
Chris@0
|
89 // We start a new msg so save previous
|
Chris@0
|
90 // TODO: this fails when comments or contexts are added
|
Chris@0
|
91 $this->addMessage($messages, $item);
|
Chris@0
|
92 $item = $defaults;
|
Chris@0
|
93 $item['ids']['singular'] = substr($line, 7, -1);
|
Chris@14
|
94 } elseif ('msgstr "' === substr($line, 0, 8)) {
|
Chris@0
|
95 $item['translated'] = substr($line, 8, -1);
|
Chris@14
|
96 } elseif ('"' === $line[0]) {
|
Chris@0
|
97 $continues = isset($item['translated']) ? 'translated' : 'ids';
|
Chris@0
|
98
|
Chris@17
|
99 if (\is_array($item[$continues])) {
|
Chris@0
|
100 end($item[$continues]);
|
Chris@0
|
101 $item[$continues][key($item[$continues])] .= substr($line, 1, -1);
|
Chris@0
|
102 } else {
|
Chris@0
|
103 $item[$continues] .= substr($line, 1, -1);
|
Chris@0
|
104 }
|
Chris@14
|
105 } elseif ('msgid_plural "' === substr($line, 0, 14)) {
|
Chris@0
|
106 $item['ids']['plural'] = substr($line, 14, -1);
|
Chris@14
|
107 } elseif ('msgstr[' === substr($line, 0, 7)) {
|
Chris@0
|
108 $size = strpos($line, ']');
|
Chris@0
|
109 $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1);
|
Chris@0
|
110 }
|
Chris@0
|
111 }
|
Chris@0
|
112 // save last item
|
Chris@17
|
113 if (!\in_array('fuzzy', $flags)) {
|
Chris@0
|
114 $this->addMessage($messages, $item);
|
Chris@0
|
115 }
|
Chris@0
|
116 fclose($stream);
|
Chris@0
|
117
|
Chris@0
|
118 return $messages;
|
Chris@0
|
119 }
|
Chris@0
|
120
|
Chris@0
|
121 /**
|
Chris@0
|
122 * Save a translation item to the messages.
|
Chris@0
|
123 *
|
Chris@0
|
124 * A .po file could contain by error missing plural indexes. We need to
|
Chris@0
|
125 * fix these before saving them.
|
Chris@0
|
126 */
|
Chris@0
|
127 private function addMessage(array &$messages, array $item)
|
Chris@0
|
128 {
|
Chris@17
|
129 if (\is_array($item['translated'])) {
|
Chris@0
|
130 $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]);
|
Chris@0
|
131 if (isset($item['ids']['plural'])) {
|
Chris@0
|
132 $plurals = $item['translated'];
|
Chris@0
|
133 // PO are by definition indexed so sort by index.
|
Chris@0
|
134 ksort($plurals);
|
Chris@0
|
135 // Make sure every index is filled.
|
Chris@0
|
136 end($plurals);
|
Chris@0
|
137 $count = key($plurals);
|
Chris@0
|
138 // Fill missing spots with '-'.
|
Chris@0
|
139 $empties = array_fill(0, $count + 1, '-');
|
Chris@0
|
140 $plurals += $empties;
|
Chris@0
|
141 ksort($plurals);
|
Chris@0
|
142 $messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals));
|
Chris@0
|
143 }
|
Chris@0
|
144 } elseif (!empty($item['ids']['singular'])) {
|
Chris@0
|
145 $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']);
|
Chris@0
|
146 }
|
Chris@0
|
147 }
|
Chris@0
|
148 }
|