public function parse(InterfaceHandler $handle = null)
{
if ($handle === null) {
if ($this->sourceHandle === null) {
throw new \InvalidArgumentException('Must provide a valid InterfaceHandler');
} else {
$handle = $this->sourceHandle;
}
}
$headers = array();
$hash = array();
$entry = array();
$justNewEntry = false;
// A new entry has been just inserted.
$firstLine = true;
$lastPreviousKey = null;
// Used to remember last key in a multiline previous entry.
$state = null;
$lineNumber = 0;
while (!$handle->ended()) {
$line = trim($handle->getNextLine());
$split = preg_split('/\\s+/ ', $line, 2);
$key = $split[0];
// If a blank line is found, or a new msgid when already got one
if ($line === '' || $key == 'msgid' && isset($entry['msgid'])) {
// Two consecutive blank lines
if ($justNewEntry) {
$lineNumber++;
continue;
}
if ($firstLine) {
$firstLine = false;
if (self::isHeader($entry)) {
array_shift($entry['msgstr']);
$headers = $entry['msgstr'];
} else {
$hash[] = $entry;
}
} else {
// A new entry is found!
$hash[] = $entry;
}
$entry = array();
$state = null;
$justNewEntry = true;
$lastPreviousKey = null;
if ($line === '') {
$lineNumber++;
continue;
}
}
$justNewEntry = false;
$data = isset($split[1]) ? $split[1] : null;
switch ($key) {
// Flagged translation
case '#,':
$entry['flags'] = preg_split('/,\\s*/', $data);
break;
// # Translator comments
// # Translator comments
case '#':
$entry['tcomment'] = !isset($entry['tcomment']) ? array() : $entry['tcomment'];
$entry['tcomment'][] = $data;
break;
// #. Comments extracted from source code
// #. Comments extracted from source code
case '#.':
$entry['ccomment'] = !isset($entry['ccomment']) ? array() : $entry['ccomment'];
$entry['ccomment'][] = $data;
break;
// Reference
// Reference
case '#:':
$entry['reference'][] = addslashes($data);
break;
case '#|':
// #| Previous untranslated string
// #| Previous untranslated string
case '#~':
// #~ Old entry
// #~ Old entry
case '#~|':
// #~| Previous-Old untranslated string. Reported by @Cellard
switch ($key) {
case '#|':
$key = 'previous';
break;
case '#~':
$key = 'obsolete';
break;
case '#~|':
$key = 'previous-obsolete';
break;
}
$tmpParts = explode(' ', $data);
$tmpKey = $tmpParts[0];
if (!in_array($tmpKey, array('msgid', 'msgid_plural', 'msgstr', 'msgctxt'))) {
$tmpKey = $lastPreviousKey;
// If there is a multiline previous string we must remember what key was first line.
$str = $data;
} else {
$str = implode(' ', array_slice($tmpParts, 1));
}
$entry[$key] = isset($entry[$key]) ? $entry[$key] : array('msgid' => array(), 'msgstr' => array());
if (strpos($key, 'obsolete') !== false) {
$entry['obsolete'] = true;
switch ($tmpKey) {
case 'msgid':
$entry['msgid'][] = $str;
$lastPreviousKey = $tmpKey;
break;
case 'msgstr':
if ($str == "\"\"") {
$entry['msgstr'][] = trim($str, '"');
} else {
$entry['msgstr'][] = $str;
}
$lastPreviousKey = $tmpKey;
break;
default:
break;
}
}
if ($key !== 'obsolete') {
switch ($tmpKey) {
case 'msgid':
case 'msgid_plural':
case 'msgstr':
$entry[$key][$tmpKey][] = $str;
$lastPreviousKey = $tmpKey;
break;
default:
$entry[$key][$tmpKey] = $str;
break;
}
}
break;
// context
// Allows disambiguations of different messages that have same msgid.
// Example:
//
// #: tools/observinglist.cpp:700
// msgctxt "First letter in 'Scope'"
// msgid "S"
// msgstr ""
//
// #: skycomponents/horizoncomponent.cpp:429
// msgctxt "South"
// msgid "S"
// msgstr ""
// context
// Allows disambiguations of different messages that have same msgid.
// Example:
//
// #: tools/observinglist.cpp:700
// msgctxt "First letter in 'Scope'"
// msgid "S"
// msgstr ""
//
// #: skycomponents/horizoncomponent.cpp:429
// msgctxt "South"
// msgid "S"
// msgstr ""
case 'msgctxt':
// untranslated-string
// untranslated-string
case 'msgid':
// untranslated-string-plural
// untranslated-string-plural
case 'msgid_plural':
$state = $key;
$entry[$state][] = $data;
break;
// translated-string
// translated-string
case 'msgstr':
$state = 'msgstr';
$entry[$state][] = $data;
break;
default:
if (strpos($key, 'msgstr[') !== false) {
// translated-string-case-n
$state = $key;
$entry[$state][] = $data;
} else {
// "multiline" lines
switch ($state) {
case 'msgctxt':
case 'msgid':
case 'msgid_plural':
case strpos($state, 'msgstr[') !== false:
if (is_string($entry[$state])) {
// Convert it to array
$entry[$state] = array($entry[$state]);
}
$entry[$state][] = $line;
break;
case 'msgstr':
// Special fix where msgid is ""
if ($entry['msgid'] == "\"\"") {
$entry['msgstr'][] = trim($line, '"');
} else {
$entry['msgstr'][] = $line;
}
break;
default:
throw new \Exception('PoParser: Parse error! Unknown key "' . $key . '" on line ' . ($lineNumber + 1));
}
}
break;
}
$lineNumber++;
}
$handle->close();
// add final entry
if ($state == 'msgstr') {
$hash[] = $entry;
}
// - Cleanup header data
$this->headers = array();
foreach ($headers as $header) {
$header = $this->clean($header);
$this->headers[] = "\"" . preg_replace("/\\n/", '\\n', $header) . "\"";
}
// - Cleanup data,
// - merge multiline entries
// - Reindex hash for ksort
$temp = $hash;
$this->entries = array();
foreach ($temp as $entry) {
foreach ($entry as &$v) {
$or = $v;
$v = $this->clean($v);
if ($v === false) {
// parse error
throw new \Exception('PoParser: Parse error! poparser::clean returned false on "' . htmlspecialchars($or) . '"');
}
}
// check if msgid and a key starting with msgstr exists
if (isset($entry['msgid']) && count(preg_grep('/^msgstr/', array_keys($entry)))) {
$id = $this->getEntryId($entry);
$this->entries[$id] = $entry;
}
}
return $this->entries;
}