private PoStreamReader::readLine()
Reads a line from the PO stream and stores data internally.
Expands $this->_current_item based on new data for the current item. If this line ends the current item, it is saved with setItemFromArray() with data from $this->_current_item.
An internal state machine is maintained in this reader using $this->_context as the reading state. PO items are in between COMMENT states (when items have at least one line or comment in between them) or indicated by MSGSTR or MSGSTR_ARR followed immediately by an MSGID or MSGCTXT (when items closely follow each other).
Return value
FALSE if an error was logged, NULL otherwise. The errors are considered non-blocking, so reading can continue, while the errors are collected for later presentation.
File
- core/lib/Drupal/Component/Gettext/PoStreamReader.php, line 245
Class
- PoStreamReader
- Implements Gettext PO stream reader.
Namespace
Drupal\Component\Gettext
Code
private function readLine() { // Read a line and set the stream finished indicator if it was not // possible anymore. $line = fgets($this->_fd); $this->_finished = ($line === FALSE); if (!$this->_finished) { if ($this->_line_number == 0) { // The first line might come with a UTF-8 BOM, which should be removed. $line = str_replace("\xEF\xBB\xBF", '', $line); // Current plurality for 'msgstr[]'. $this->_current_plural_index = 0; } // Track the line number for error reporting. $this->_line_number++; // Initialize common values for error logging. $log_vars = array( '%uri' => $this->getURI(), '%line' => $this->_line_number, ); // Trim away the linefeed. \\n might appear at the end of the string if // another line continuing the same string follows. We can remove that. $line = trim(strtr($line, array("\\\n" => ""))); if (!strncmp('#', $line, 1)) { // Lines starting with '#' are comments. if ($this->_context == 'COMMENT') { // Already in comment context, add to current comment. $this->_current_item['#'][] = substr($line, 1); } elseif (($this->_context == 'MSGSTR') || ($this->_context == 'MSGSTR_ARR')) { // We are currently in string context, save current item. $this->setItemFromArray($this->_current_item); // Start a new entry for the comment. $this->_current_item = array(); $this->_current_item['#'][] = substr($line, 1); $this->_context = 'COMMENT'; return; } else { // A comment following any other context is a syntax error. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: "msgstr" was expected but not found on line %line.', $log_vars); return FALSE; } return; } elseif (!strncmp('msgid_plural', $line, 12)) { // A plural form for the current source string. if ($this->_context != 'MSGID') { // A plural form can only be added to an msgid directly. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: "msgid_plural" was expected but not found on line %line.', $log_vars); return FALSE; } // Remove 'msgid_plural' and trim away whitespace. $line = trim(substr($line, 12)); // Only the plural source string is left, parse it. $quoted = $this->parseQuoted($line); if ($quoted === FALSE) { // The plural form must be wrapped in quotes. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains a syntax error on line %line.', $log_vars); return FALSE; } // Append the plural source to the current entry. if (is_string($this->_current_item['msgid'])) { // The first value was stored as string. Now we know the context is // plural, it is converted to array. $this->_current_item['msgid'] = array($this->_current_item['msgid']); } $this->_current_item['msgid'][] = $quoted; $this->_context = 'MSGID_PLURAL'; return; } elseif (!strncmp('msgid', $line, 5)) { // Starting a new message. if (($this->_context == 'MSGSTR') || ($this->_context == 'MSGSTR_ARR')) { // We are currently in string context, save current item. $this->setItemFromArray($this->_current_item); // Start a new context for the msgid. $this->_current_item = array(); } elseif ($this->_context == 'MSGID') { // We are currently already in the context, meaning we passed an id with no data. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: "msgid" is unexpected on line %line.', $log_vars); return FALSE; } // Remove 'msgid' and trim away whitespace. $line = trim(substr($line, 5)); // Only the message id string is left, parse it. $quoted = $this->parseQuoted($line); if ($quoted === FALSE) { // The message id must be wrapped in quotes. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: invalid format for "msgid" on line %line.', $log_vars, $log_vars); return FALSE; } $this->_current_item['msgid'] = $quoted; $this->_context = 'MSGID'; return; } elseif (!strncmp('msgctxt', $line, 7)) { // Starting a new context. if (($this->_context == 'MSGSTR') || ($this->_context == 'MSGSTR_ARR')) { // We are currently in string context, save current item. $this->setItemFromArray($this->_current_item); $this->_current_item = array(); } elseif (!empty($this->_current_item['msgctxt'])) { // A context cannot apply to another context. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: "msgctxt" is unexpected on line %line.', $log_vars); return FALSE; } // Remove 'msgctxt' and trim away whitespaces. $line = trim(substr($line, 7)); // Only the msgctxt string is left, parse it. $quoted = $this->parseQuoted($line); if ($quoted === FALSE) { // The context string must be quoted. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: invalid format for "msgctxt" on line %line.', $log_vars); return FALSE; } $this->_current_item['msgctxt'] = $quoted; $this->_context = 'MSGCTXT'; return; } elseif (!strncmp('msgstr[', $line, 7)) { // A message string for a specific plurality. if (($this->_context != 'MSGID') && ($this->_context != 'MSGCTXT') && ($this->_context != 'MSGID_PLURAL') && ($this->_context != 'MSGSTR_ARR')) { // Plural message strings must come after msgid, msgxtxt, // msgid_plural, or other msgstr[] entries. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: "msgstr[]" is unexpected on line %line.', $log_vars); return FALSE; } // Ensure the plurality is terminated. if (strpos($line, ']') === FALSE) { $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: invalid format for "msgstr[]" on line %line.', $log_vars); return FALSE; } // Extract the plurality. $frombracket = strstr($line, '['); $this->_current_plural_index = substr($frombracket, 1, strpos($frombracket, ']') - 1); // Skip to the next whitespace and trim away any further whitespace, // bringing $line to the message text only. $line = trim(strstr($line, " ")); $quoted = $this->parseQuoted($line); if ($quoted === FALSE) { // The string must be quoted. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: invalid format for "msgstr[]" on line %line.', $log_vars); return FALSE; } if (!isset($this->_current_item['msgstr']) || !is_array($this->_current_item['msgstr'])) { $this->_current_item['msgstr'] = array(); } $this->_current_item['msgstr'][$this->_current_plural_index] = $quoted; $this->_context = 'MSGSTR_ARR'; return; } elseif (!strncmp("msgstr", $line, 6)) { // A string pair for an msgid (with optional context). if (($this->_context != 'MSGID') && ($this->_context != 'MSGCTXT')) { // Strings are only valid within an id or context scope. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: "msgstr" is unexpected on line %line.', $log_vars); return FALSE; } // Remove 'msgstr' and trim away away whitespaces. $line = trim(substr($line, 6)); // Only the msgstr string is left, parse it. $quoted = $this->parseQuoted($line); if ($quoted === FALSE) { // The string must be quoted. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: invalid format for "msgstr" on line %line.', $log_vars); return FALSE; } $this->_current_item['msgstr'] = $quoted; $this->_context = 'MSGSTR'; return; } elseif ($line != '') { // Anything that is not a token may be a continuation of a previous token. $quoted = $this->parseQuoted($line); if ($quoted === FALSE) { // This string must be quoted. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: string continuation expected on line %line.', $log_vars); return FALSE; } // Append the string to the current item. if (($this->_context == 'MSGID') || ($this->_context == 'MSGID_PLURAL')) { if (is_array($this->_current_item['msgid'])) { // Add string to last array element for plural sources. $last_index = count($this->_current_item['msgid']) - 1; $this->_current_item['msgid'][$last_index] .= $quoted; } else { // Singular source, just append the string. $this->_current_item['msgid'] .= $quoted; } } elseif ($this->_context == 'MSGCTXT') { // Multiline context name. $this->_current_item['msgctxt'] .= $quoted; } elseif ($this->_context == 'MSGSTR') { // Multiline translation string. $this->_current_item['msgstr'] .= $quoted; } elseif ($this->_context == 'MSGSTR_ARR') { // Multiline plural translation string. $this->_current_item['msgstr'][$this->_current_plural_index] .= $quoted; } else { // No valid context to append to. $this->_errors[] = SafeMarkup::format('The translation stream %uri contains an error: unexpected string on line %line.', $log_vars); return FALSE; } return; } } // Empty line read or EOF of PO stream, close out the last entry. if (($this->_context == 'MSGSTR') || ($this->_context == 'MSGSTR_ARR')) { $this->setItemFromArray($this->_current_item); $this->_current_item = array(); } elseif ($this->_context != 'COMMENT') { $this->_errors[] = SafeMarkup::format('The translation stream %uri ended unexpectedly at line %line.', $log_vars); return FALSE; } }
Please login to continue.