FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Preserve empty message contexts in PO and MO output by fhgffy · Pull Request #1373 · python-babel/babel · GitHub

Repository navigation

Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (4) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
3 changes: 2 additions & 1 deletion babel/messages/mofile.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ def write_mo(fileobj: SupportsWrite[bytes], catalog: Catalog, use_fuzzy: bool =
else:
msgid = message.id.encode(catalog.charset)
msgstr = message.string.encode(catalog.charset)
if message.context:
# 2026-10-10: An empty context differs from an absent context.
if message.context is not None:
msgid = b'\x04'.join([message.context.encode(catalog.charset), msgid])
offsets.append((len(ids), len(msgid), len(strs), len(msgstr)))
ids += msgid + b'\x00'
Expand Down
6 changes: 4 additions & 2 deletions babel/messages/pofile.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,8 @@ def _format_conflict(key: str | tuple[str, str], conflicts: list[ConflictInfo],

def _format_message(message, prefix=''):
if isinstance(message.id, (list, tuple)):
if message.context:
# 2026-10-10: Preserve explicitly empty contexts.
if message.context is not None:
yield f"{prefix}msgctxt {normalize(message.context, prefix=prefix, width=width)}\n"
yield f"{prefix}msgid {normalize(message.id[0], prefix=prefix, width=width)}\n"
yield f"{prefix}msgid_plural {normalize(message.id[1], prefix=prefix, width=width)}\n"
Expand All @@ -694,7 +695,8 @@ def _format_message(message, prefix=''):
string = ''
yield f"{prefix}msgstr[{idx:d}] {normalize(string, prefix=prefix, width=width)}\n"
else:
if message.context:
# 2026-10-10: Preserve explicitly empty contexts.
if message.context is not None:
yield f"{prefix}msgctxt {normalize(message.context, prefix=prefix, width=width)}\n"
yield f"{prefix}msgid {normalize(message.id, prefix=prefix, width=width)}\n"
yield f"{prefix}msgstr {normalize(message.string or '', prefix=prefix, width=width)}\n"
Expand Down
23 changes: 23 additions & 0 deletions tests/messages/test_mofile.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,26 @@ def test_read_mo_decodes_message_context() -> None:
assert message.context == 'ほげ'
assert message.string == 'åäöÅÄÖ'
assert not catalog.get('x') # Not without the context


def test_empty_message_context():
# 2026-10-10: Empty contexts must remain distinct from absent contexts.
catalog = Catalog()
for context, label in [(None, 'plain'), ('', 'empty'), ('menu', 'menu')]:
catalog.add('foo', label, context=context)
catalog.add(('item', 'items'), (label + ' one', label + ' many'), context=context)
buf = BytesIO()
mofile.write_mo(buf, catalog)
buf.seek(0)
restored = mofile.read_mo(buf)
assert len(restored) == 6
for context, label in [(None, 'plain'), ('', 'empty'), ('menu', 'menu')]:
assert restored.get('foo', context=context).string == label
assert restored.get('item', context=context).string == [label + ' one', label + ' many']
buf.seek(0)
translations = Translations(fp=buf)
assert translations.gettext('foo') == 'plain'
assert translations.pgettext('', 'foo') == 'empty'
assert translations.pgettext('menu', 'foo') == 'menu'
assert translations.ngettext('item', 'items', 2) == 'plain many'
assert translations.npgettext('', 'item', 'items', 2) == 'empty many'
18 changes: 18 additions & 0 deletions tests/messages/test_pofile.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,21 @@ def test_issue_1134(case: str, abort_invalid: bool):
output = pofile.read_po(buf)
assert len(output) == 1
assert output["foo"].string in ((''), ('', ''))


# 2026-10-10: PO roundtrips must preserve empty singular and plural contexts.
@pytest.mark.parametrize('pluralizable', [False, True])
def test_empty_message_context(pluralizable):
catalog = Catalog()
msgid = ('foo', 'foos') if pluralizable else 'foo'
for context, label in [(None, 'plain'), ('', 'empty'), ('menu', 'menu')]:
translation = (label + ' one', label + ' many') if pluralizable else label
catalog.add(msgid, translation, context=context)
buf = BytesIO()
pofile.write_po(buf, catalog)
buf.seek(0)
restored = pofile.read_po(buf)
assert len(restored) == 3
for context, label in [(None, 'plain'), ('', 'empty'), ('menu', 'menu')]:
translation = (label + ' one', label + ' many') if pluralizable else label
assert restored.get('foo', context=context).string == translation

Back | FazBrowse Home | New Git URL