1: <?php
2:
3: class QPoParserException extends QCallerException {}
4:
5: class QTranslationPoParser implements QTranslationBase {
6: public static function Initialize() {
7: return self::Load(QApplication::$LanguageCode, QApplication::$CountryCode);
8: }
9:
10: public static function Load($strLanguageCode = null, $strCountryCode = null) {
11: $objLanguageObject = null;
12: if ($strLanguageCode) {
13: if ($strCountryCode) {
14: $strCode = sprintf('%s_%s', $strLanguageCode, $strCountryCode);
15: $strLanguageFiles = array(
16: __QCUBED_CORE__ . '/i18n/' . $strLanguageCode . '.po',
17: __QCUBED_CORE__ . '/i18n/' . $strCode . '.po',
18: __QI18N_PO_PATH__ . '/' . $strLanguageCode . '.po',
19: __QI18N_PO_PATH__ . '/' . $strCode . '.po'
20: );
21: } else {
22: $strCode = $strLanguageCode;
23: $strLanguageFiles = array(
24: __QCUBED_CORE__ . '/i18n/' . $strLanguageCode . '.po',
25: __QI18N_PO_PATH__ . '/' . $strLanguageCode . '.po'
26: );
27: }
28:
29:
30: $objCache = new QCache('i18n.po', $strCode, 'i18n', $strLanguageFiles);
31:
32:
33: $strData = $objCache->GetData();
34: if ($strData) {
35: $objLanguageObject = unserialize($strData);
36:
37:
38: } else {
39: $objLanguage = new QTranslationPoParser();
40:
41: foreach ($strLanguageFiles as $strLanguageFile) {
42: if (file_exists($strLanguageFile)) {
43: try {
44:
45: $objLanguage->ParsePoData(file_get_contents($strLanguageFile));
46: } catch (QPoParserException $objExc) {
47: $objExc->setMessage('Invalid Language File: ' . $strLanguageFile . ': ' . $objExc->getMessage());
48: $objExc->IncrementOffset();
49: throw $objExc;
50: }
51: }
52: }
53: $objLanguageObject = $objLanguage;
54: $objCache->SaveData(serialize($objLanguage));
55: }
56: }
57: return $objLanguageObject;
58:
59: }
60:
61: const PoParseStateNone = 0;
62: const PoParseStateMessageIdStart = 1;
63: const PoParseStateMessageId = 2;
64: const PoParseStateMessageStringStart = 3;
65: const PoParseStateMessageString = 4;
66:
67: protected static function UnescapeContent($strContent) {
68: $intLength = strlen($strContent);
69: $strToReturn = '';
70: $blnEscape = false;
71:
72: for ($intIndex = 0; $intIndex < $intLength; $intIndex++) {
73: if ($blnEscape) {
74: switch ($strContent[$intIndex]) {
75: case 'n':
76: $blnEscape = false;
77: $strToReturn .= "\n";
78: break;
79: case 'r':
80: $blnEscape = false;
81: $strToReturn .= "\r";
82: break;
83: case 't':
84: $blnEscape = false;
85: $strToReturn .= " ";
86: break;
87: case '\\':
88: $blnEscape = false;
89: $strToReturn .= '\\';
90: break;
91: case '"':
92: $blnEscape = false;
93: $strToReturn .= '"';
94: break;
95: case "'":
96: $blnEscape = false;
97: $strToReturn .= "'";
98: break;
99: default:
100: $blnEscape = false;
101: $strToReturn .= '\\' . $strContent[$intIndex];
102: break;
103: }
104: } else {
105: if ($strContent[$intIndex] == '\\')
106: $blnEscape = true;
107: else
108: $strToReturn .= $strContent[$intIndex];
109: }
110: }
111:
112: if ($blnEscape)
113: return false;
114:
115: $strToReturn = str_replace("\r", '', $strToReturn);
116: return $strToReturn;
117: }
118:
119: protected function ParsePoData($strPoData) {
120: $strPoData = str_replace("\r", '', trim($strPoData));
121: $strPoLines = explode("\n", $strPoData);
122:
123: $strMatches = array();
124:
125: $intState = QTranslationPoParser::PoParseStateNone;
126: $intLineCount = count($strPoLines);
127:
128: if (strlen($strPoLines[0]) == 0)
129: return;
130:
131: for ($intLineNumber = 0; $intLineNumber < $intLineCount; $intLineNumber++) {
132: $strPoLine = $strPoLines[$intLineNumber] = trim($strPoLines[$intLineNumber]);
133:
134: if (strlen($strPoLine) && (QString::FirstCharacter($strPoLine) != '#')) {
135: switch ($intState) {
136: case QTranslationPoParser::PoParseStateNone:
137: $intCount = preg_match_all('/msgid(_[a-z0-9]+)?[\s]+"([\S ]*)"/i', $strPoLine, $strMatches);
138: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
139: $intLineNumber--;
140: $intState = QTranslationPoParser::PoParseStateMessageIdStart;
141: } else
142: throw new QPoParserException('Invalid content for PoParseStateNone on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
143: break;
144:
145: case QTranslationPoParser::PoParseStateMessageIdStart:
146: $intCount = preg_match_all('/msgid(_[a-z0-9]+)?[\s]+"([\S ]*)"/i', $strPoLine, $strMatches);
147: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
148: $strMessageId = array('', '', '', '', '', '', '');
149: $strMessageString = array('', '', '', '', '', '', '');
150: $intArrayIndex = 0;
151:
152: $strContent = QTranslationPoParser::UnescapeContent($strMatches[2][0]);
153: if ($strContent === false)
154: throw new QPoParserException('Invalid content on Line ' . ($intLineNumber + 1));
155: $strMessageId[$intArrayIndex] = $strContent;
156: $intState = QTranslationPoParser::PoParseStateMessageId;
157: } else
158: throw new QPoParserException('Invalid content for PoParseStateMessageIdStart on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
159: break;
160:
161: case QTranslationPoParser::PoParseStateMessageId:
162: $intCount = preg_match_all('/msgid(_[a-z0-9]+)[\s]+"([\S ]*)"/i', $strPoLine, $strMatches);
163: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
164: if (strlen(trim($strMessageId[$intArrayIndex])) == 0)
165: throw new QPoParserException('No MsgId content for current MsgId on Line ' . ($intLineNumber) . ': ' . $strPoLine);
166: $intArrayIndex++;
167: $strContent = QTranslationPoParser::UnescapeContent($strMatches[2][0]);
168: if ($strContent === false)
169: throw new QPoParserException('Invalid content on Line ' . ($intLineNumber + 1));
170: $strMessageId[$intArrayIndex] = $strContent;
171: break;
172: }
173:
174: $intCount = preg_match_all('/"([\S ]*)"/', $strPoLine, $strMatches);
175: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
176: $strContent = QTranslationPoParser::UnescapeContent($strMatches[1][0]);
177: if ($strContent === false)
178: throw new QPoParserException('Invalid content on Line ' . ($intLineNumber + 1));
179: $strMessageId[$intArrayIndex] .= $strContent;
180: break;
181: }
182:
183: $intCount = preg_match_all('/msgstr(\[[0-9]+\])?[\s]+"([\S ]*)"/i', $strPoLine, $strMatches);
184: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
185: if (strlen(trim($strMessageId[$intArrayIndex])) == 0)
186: throw new QPoParserException('No MsgId content for current MsgId on Line ' . ($intLineNumber) . ': ' . $strPoLine);
187: $intLineNumber--;
188: $intState = QTranslationPoParser::PoParseStateMessageStringStart;
189: break;
190: }
191:
192: throw new QPoParserException('Invalid content for PoParseStateMessageId on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
193:
194: case QTranslationPoParser::PoParseStateMessageStringStart:
195: $intCount = preg_match_all('/msgstr(\[[0-9]+\])?[\s]+"([\S ]*)"/i', $strPoLine, $strMatches);
196: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
197: $intArrayIndex = 0;
198:
199: if (strlen($strMatches[1][0]))
200: $intArrayIndex = intval(substr($strMatches[1][0], 1, strlen($strMatches[1][0]) - 2));
201:
202: $strContent = QTranslationPoParser::UnescapeContent($strMatches[2][0]);
203: if ($strContent === false)
204: throw new QPoParserException('Invalid content on Line ' . ($intLineNumber + 1));
205: $strMessageString[$intArrayIndex] = $strContent;
206: $intState = QTranslationPoParser::PoParseStateMessageString;
207: } else
208: throw new QPoParserException('Invalid content for PoParseStateMessageStringStart on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
209: break;
210:
211:
212: case QTranslationPoParser::PoParseStateMessageString:
213: $intCount = preg_match_all('/msgid(_[a-z0-9]+)?[\s]+"([\S ]*)"/i', $strPoLine, $strMatches);
214: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
215: for ($intIndex = 0; $intIndex < count($strMessageId); $intIndex++)
216: if (strlen(trim($strMessageId[$intIndex]))) {
217: if (!strlen(trim($strMessageString[$intIndex]))) {
218: $this->SetTranslation($strMessageId[$intIndex], "");
219: }
220: $this->SetTranslation($strMessageId[$intIndex], $strMessageString[$intIndex]);
221: }
222:
223: $intLineNumber--;
224: $intState = QTranslationPoParser::PoParseStateMessageIdStart;
225: break;
226: }
227:
228: $intCount = preg_match_all('/"([\S ]*)"/', $strPoLine, $strMatches);
229: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
230: $strContent = QTranslationPoParser::UnescapeContent($strMatches[1][0]);
231: if ($strContent === false)
232: throw new QPoParserException('Invalid content on Line ' . ($intLineNumber + 1));
233: $strMessageString[$intArrayIndex] .= $strContent;
234: break;
235: }
236:
237: $intCount = preg_match_all('/msgstr(\[[0-9]+\])?[\s]+"([\S ]*)"/i', $strPoLine, $strMatches);
238: if ($intCount && ($strMatches[0][0] == $strPoLine)) {
239:
240: if (strlen($strMatches[1][0]))
241: $intArrayIndex = intval(substr($strMatches[1][0], 1, strlen($strMatches[1][0]) - 2));
242: else
243: throw new QPoParserException('No index specified for alternate MsgStr for PoParseStateMessageString on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
244:
245: if (strlen(trim($strMessageId[$intArrayIndex])) == 0)
246: throw new QPoParserException('No MsgId for MsgStr' . $strMatches[1][0] . ' for PoParseStateMessageString on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
247:
248: $strContent = QTranslationPoParser::UnescapeContent($strMatches[2][0]);
249: if ($strContent === false)
250: throw new QPoParserException('Invalid content on Line ' . ($intLineNumber + 1));
251: $strMessageString[$intArrayIndex] = $strContent;
252: break;
253: }
254:
255: throw new QPoParserException('Invalid content for PoParseStateMessageString on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
256:
257: default:
258: throw new QPoParserException('Invalid PoParseState on Line ' . ($intLineNumber + 1) . ': ' . $strPoLine);
259: }
260: }
261: }
262:
263: for ($intIndex = 0; $intIndex < count($strMessageId); $intIndex++) {
264: if (strlen(trim($strMessageId[$intIndex]))) {
265: if (!strlen(trim($strMessageString[$intIndex]))) {
266: $this->SetTranslation($strMessageId[$intIndex], "");
267: }
268: $this->SetTranslation($strMessageId[$intIndex], $strMessageString[$intIndex]);
269: }
270: }
271: }
272:
273: protected $strTranslationArray = array();
274:
275: protected function SetTranslation($strToken, $strTranslatedText) {
276: $this->strTranslationArray[$strToken] = $strTranslatedText;
277: }
278:
279: public function TranslateToken($strToken) {
280: $strCleanToken = str_replace("\r", '', $strToken);
281: if (array_key_exists($strCleanToken, $this->strTranslationArray)) {
282: return $this->strTranslationArray[$strCleanToken];
283: } else {
284: return $strToken;
285: }
286: }
287:
288: public function VarDump() {
289: $strToReturn = '';
290: foreach ($this->strTranslationArray as $strKey=>$strValue) {
291: $strKey = str_replace("\n", '\\n', addslashes(QApplication::HtmlEntities($strKey)));
292: $strValue = str_replace("\n", '\\n', addslashes(QApplication::HtmlEntities($strValue)));
293: $strToReturn .= sprintf("\"%s\"\n\"%s\"\n\n", $strKey, $strValue);
294: }
295: return $strToReturn;
296: }
297: }
298: