The msgctxt introduction patch (#334283: Add msgctxt-type context to t()) changed the last string save condition to require a non-empty msgstr value at the end. While none of the other msgstr parsing requires an actual msgstr value to present to call the save callback, this one does, so if the last msgstr translation item in the file is empty, the file is reported broken by Drupal (even though all previous strings were imported and the file is not broken at all).

Drupal 6 had this:

  // End of PO file, flush last entry
  if (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) {
    _locale_import_one_string($op, $current, $mode, $lang, $file, $group);
  }
  elseif ($context != "COMMENT") {
    _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
    return FALSE;
  }

Drupal 7 has this:

  // End of PO file, flush last entry
  if (!empty($current) && !empty($current['msgstr'])) {
    _locale_import_one_string($op, $current, $mode, $lang, $file, $group);
  }
  elseif ($context != "COMMENT") {
    _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
    return FALSE;
  }

So what happens is that if the last string has an empty msgstr (the last source has no translation), it flows into the unexpected end of file condition, which does match, because we are not yet in the comment context (there can be multiple consecutive msgstr lines so an msgstr line still keeps us in this context).

The solution is to treat the last content like any previous one. Just pass it on to the save callback even if it was empty. Suggested:

  // End of PO file, flush last entry.
  if (!empty($current)) {
    _locale_import_one_string($op, $current, $mode, $lang, $file, $group);
  }
  elseif ($context != "COMMENT") {
    _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
    return FALSE;
  }

Apart from fixing this bug, it also makes it consistent with other strings, where Drupal core stores the source string even for empty translations, so you can use .po files to fill up your translation database for strings which did not appear yet on the site, so you know your translation status better.

There were multiple issues submitted for this on localization server, which uses this same code in Drupal 6 so it is able to parse .po files with contexts. The master bug for it was #561296: Error message when importing .po file where the last string is untranslated with many others marked as duplicates of that.

Comments

gábor hojtsy’s picture

Alternatively, we can also go back to the D6 code here. I don't know why it needed to be changed here. Going back to the D6 code would allow us to recognize broken .po files which only have a msgid but then no msgstr, since they'd have a partially filled in $current.

gábor hojtsy’s picture

BTW the state machine in D7 still uses this conditional to start a new $current:

      elseif (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) { // End current entry, start a new one
        _l10n_community_import_one_string($current, $langcode, $is_suggestion, $uid);
        $current = array();
        $current["#"][] = substr($line, 1);
        $context = "COMMENT";
      }
gábor hojtsy’s picture

Status: Needs review » Needs work

Discussed this with Damien, and he initially assumed the file could end in a different context, but he verified via http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files that the file can actually only end in the contexts used in D6. So we can go back to that value. I'll roll a different patch later.

gábor hojtsy’s picture

Status: Needs work » Needs review
StatusFileSize
new703 bytes

Updated patch included as per discussion detailed above with Damien.

gábor hojtsy’s picture

StatusFileSize
new5.59 KB

With added tests and fixing some glaring errors in the locale tests.

gábor hojtsy’s picture

Oh, well, looks like importation is a valid word after all: http://www.merriam-webster.com/dictionary/importation So we can ignore the first four hunks of the patch. Duh.

Status: Needs review » Needs work

The last submitted patch failed testing.

pasqualle’s picture

Status: Needs work » Needs review
StatusFileSize
new5.41 KB

in the test the number of imported strings changed from 2 to 1

gábor hojtsy’s picture

Looks good, so as long as tests pass I think would be great to commit.

pasqualle’s picture

Status: Needs review » Needs work

Test failed, but that test seems wrong to me. If the imported text does not have translation, then it won't be imported at all. So it does not show up in search..

gábor hojtsy’s picture

Well, it used to be that it did import empty strings (so you can fill up a database with strings to translate). Not sure when that changed.

tobiasb’s picture

StatusFileSize
new5.91 KB
new5.13 KB

I added 2 different patches. One with extended tests und one without the buggy tests.

tobiasb’s picture

Status: Needs work » Needs review
StatusFileSize
new4.33 KB
new5.91 KB

now with the right patches

gábor hojtsy’s picture

Status: Needs review » Reviewed & tested by the community

Extended patch looks good, passes tests.

pasqualle’s picture

this looks like committed
http://drupal.org/cvs?commit=441810

gábor hojtsy’s picture

Status: Reviewed & tested by the community » Fixed

@Pasqualle: Right! And this is working well in Drupal 6 (in fact we just restored the Drupal 6 conditions here). So no need to backport either.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.