1.20.x gettext.inc | _locale_import_read_po($op, $file, $mode = NULL, $langcode = NULL) |
Parses a Gettext Portable Object file into an array.
Parameters
string $op: Storage operation type: db-store or mem-store.
stdClass $file: Backdrop file object corresponding to the PO file to import.
int $mode: Should existing translations be replaced LOCALE_IMPORT_KEEP or LOCALE_IMPORT_OVERWRITE.
string $langcode: Language code.
Return value
NULL|FALSE: NULL return value on success. FALSE upon failure.
Related topics
File
- includes/
gettext.inc, line 91 - Gettext parsing and generating API.
Code
function _locale_import_read_po($op, $file, $mode = NULL, $langcode = NULL) {
// The file will get closed by PHP on returning from this function.
$fd = fopen($file->uri, 'rb');
if (!$fd) {
_locale_import_message('The translation import failed because the file %filename could not be read.', $file);
return FALSE;
}
/*
* The parser context. Can be:
* - 'COMMENT' (#)
* - 'MSGID' (msgid)
* - 'MSGID_PLURAL' (msgid_plural)
* - 'MSGCTXT' (msgctxt)
* - 'MSGSTR' (msgstr or msgstr[])
* - 'MSGSTR_ARR' (msgstr_arg)
*/
$context = 'COMMENT';
// Current entry being read.
$current = array();
// Current plurality for 'msgstr[]'.
$plural = 0;
// Current line.
$lineno = 0;
while (!feof($fd)) {
// Refresh time limit every 10 parsed rows.
if (!($lineno % 10)) {
backdrop_set_time_limit(30);
}
// A line should not be longer than 10 * 1024.
$line = fgets($fd, 10 * 1024);
if ($lineno == 0) {
// The first line might come with a UTF-8 BOM, which should be removed.
$line = str_replace("\xEF\xBB\xBF", '', $line);
}
$lineno++;
// Trim away the linefeed.
$line = trim(strtr($line, array("\\\n" => "")));
if (!strncmp('#', $line, 1)) {
// Lines starting with '#' are comments.
if ($context == 'COMMENT') {
// Already in comment token, insert the comment.
$current['#'][] = substr($line, 1);
}
elseif (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
// We are currently in string token, close it out.
_locale_import_one_string($op, $current, $mode, $langcode, $file);
// Start a new entry for the comment.
$current = array();
$current['#'][] = substr($line, 1);
$context = 'COMMENT';
}
else {
// A comment following any other token is a syntax error.
_locale_import_message('The translation file %filename contains an error: "msgstr" was expected but not found on line %line.', $file, $lineno);
return FALSE;
}
}
elseif (!strncmp('msgid_plural', $line, 12)) {
// A plural form for the current message.
if ($context != 'MSGID') {
// A plural form cannot be added to anything else but the id directly.
_locale_import_message('The translation file %filename contains an error: "msgid_plural" was expected but not found on line %line.', $file, $lineno);
return FALSE;
}
// Remove 'msgid_plural' and trim away whitespace.
$line = trim(substr($line, 12));
// At this point, $line should now contain only the plural form.
$quoted = _locale_import_parse_quoted($line);
if ($quoted === FALSE) {
// The plural form must be wrapped in quotes.
_locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
return FALSE;
}
// Append the plural form to the current entry.
$current['msgid'] .= "\0" . $quoted;
$context = 'MSGID_PLURAL';
}
elseif (!strncmp('msgid', $line, 5)) {
// Starting a new message.
if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
// We are currently in a message string, close it out.
_locale_import_one_string($op, $current, $mode, $langcode, $file);
// Start a new context for the id.
$current = array();
}
elseif ($context == 'MSGID') {
// We are currently already in the context, meaning we passed an id with no data.
_locale_import_message('The translation file %filename contains an error: "msgid" is unexpected on line %line.', $file, $lineno);
return FALSE;
}
// Remove 'msgid' and trim away whitespace.
$line = trim(substr($line, 5));
// At this point, $line should now contain only the message id.
$quoted = _locale_import_parse_quoted($line);
if ($quoted === FALSE) {
// The message id must be wrapped in quotes.
_locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
return FALSE;
}
$current['msgid'] = $quoted;
$context = 'MSGID';
}
elseif (!strncmp('msgctxt', $line, 7)) {
// Starting a new context.
if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
// We are currently in a message, start a new one.
_locale_import_one_string($op, $current, $mode, $langcode, $file);
$current = array();
}
elseif (!empty($current['msgctxt'])) {
// A context cannot apply to another context.
_locale_import_message('The translation file %filename contains an error: "msgctxt" is unexpected on line %line.', $file, $lineno);
return FALSE;
}
// Remove 'msgctxt' and trim away whitespaces.
$line = trim(substr($line, 7));
// At this point, $line should now contain the context.
$quoted = _locale_import_parse_quoted($line);
if ($quoted === FALSE) {
// The context string must be quoted.
_locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
return FALSE;
}
$current['msgctxt'] = $quoted;
$context = 'MSGCTXT';
}
elseif (!strncmp('msgstr[', $line, 7)) {
// A message string for a specific plurality.
if (($context != 'MSGID') && ($context != 'MSGCTXT') && ($context != 'MSGID_PLURAL') && ($context != 'MSGSTR_ARR')) {
// Message strings must come after msgid, msgxtxt, msgid_plural, or other msgstr[] entries.
_locale_import_message('The translation file %filename contains an error: "msgstr[]" is unexpected on line %line.', $file, $lineno);
return FALSE;
}
// Ensure the plurality is terminated.
if (strpos($line, ']') === FALSE) {
_locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
return FALSE;
}
// Extract the plurality.
$frombracket = strstr($line, '[');
$plural = substr($frombracket, 1, strpos($frombracket, ']') - 1);
// Skip to the next whitespace and trim away any further whitespace, bringing $line to the message data.
$line = trim(strstr($line, " "));
$quoted = _locale_import_parse_quoted($line);
if ($quoted === FALSE) {
// The string must be quoted.
_locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
return FALSE;
}
$current['msgstr'][$plural] = $quoted;
$context = 'MSGSTR_ARR';
}
elseif (!strncmp("msgstr", $line, 6)) {
// A string for the an id or context.
if (($context != 'MSGID') && ($context != 'MSGCTXT')) {
// Strings are only valid within an id or context scope.
_locale_import_message('The translation file %filename contains an error: "msgstr" is unexpected on line %line.', $file, $lineno);
return FALSE;
}
// Remove 'msgstr' and trim away away whitespaces.
$line = trim(substr($line, 6));
// At this point, $line should now contain the message.
$quoted = _locale_import_parse_quoted($line);
if ($quoted === FALSE) {
// The string must be quoted.
_locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
return FALSE;
}
$current['msgstr'] = $quoted;
$context = 'MSGSTR';
}
elseif ($line != '') {
// Anything that is not a token may be a continuation of a previous token.
$quoted = _locale_import_parse_quoted($line);
if ($quoted === FALSE) {
// The string must be quoted.
_locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
return FALSE;
}
// Append the string to the current context.
if (($context == 'MSGID') || ($context == 'MSGID_PLURAL')) {
$current['msgid'] .= $quoted;
}
elseif ($context == 'MSGCTXT') {
$current['msgctxt'] .= $quoted;
}
elseif ($context == 'MSGSTR') {
$current['msgstr'] .= $quoted;
}
elseif ($context == 'MSGSTR_ARR') {
$current['msgstr'][$plural] .= $quoted;
}
else {
// No valid context to append to.
_locale_import_message('The translation file %filename contains an error: there is an unexpected string on line %line.', $file, $lineno);
return FALSE;
}
}
}
// End of PO file, closed out the last entry.
if (($context == 'MSGSTR') || ($context == 'MSGSTR_ARR')) {
_locale_import_one_string($op, $current, $mode, $langcode, $file);
}
elseif ($context != 'COMMENT') {
_locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
return FALSE;
}
return NULL;
}