Problem/Motivation
When a `.po` translation file contains plural translations with empty strings for both `msgstr[0]` and `msgstr[1]`, Drupal incorrectly treats this as a valid translation.
For example, the following `.po` entry is treated as valid:
msgid "@count comment"
msgid_plural "@count comments"
msgstr[0] ""
msgstr[1] ""
Internally, Drupal uses a special delimiter (`\x03`, defined in `PoItem::DELIMITER`) to separate plural forms. So even if both strings are empty, Drupal stores the translation as `"\x03"`, which is not technically empty. This leads to incorrect fallback behavior: the system thinks a translation exists and uses it, resulting in an empty string rendered to the user.
This affects output from `formatPlural()` when the translation exists but has no content.
Steps to reproduce
- Create a `formatPlural()` usage in code with a translatable string.
- Generate a `.po` file with `msgid` and `msgid_plural` as shown above, but leave `msgstr[0]` and `msgstr[1]` empty.
- Import the translation using the Locale UI or Drush.
- Visit the page rendering the string.
- Observe that nothing is displayed where the plural string should be.
Proposed resolution
In `LocaleLookup::resolveCacheMiss()`, strip the `PoItem::DELIMITER` (which is the ASCII control character `\x03`, or ETX β End of Text) from the translation before applying the `empty()` check. If the resulting string is still empty, consider the translation invalid and fall back to the original source string.
// Before
$value = !empty($translation->translation) ? $translation->translation : TRUE;
// After
$check = str_replace(PoItem::DELIMITER, '', $translation->translation ?? '');
$value = !empty($check) ? $translation->translation : TRUE;
Remaining tasks
- Confirm patch solves the issue across plural forms
- Add test coverage for `.po` entries with empty `msgstr[]` and a delimiter
User interface changes
None.
Introduced terminology
None.
API changes
None.
Data model changes
None.
Release notes snippet
Fixed a bug where empty plural translations containing only a delimiter (`\x03`) were incorrectly used, causing `formatPlural()` to render an empty string instead of falling back to the source string.