diff --git a/babel/messages/mofile.py b/babel/messages/mofile.py index 2413f7139..164d0b41b 100644 --- a/babel/messages/mofile.py +++ b/babel/messages/mofile.py @@ -163,7 +163,8 @@ def write_mo(fileobj: SupportsWrite[bytes], catalog: Catalog, use_fuzzy: bool = messages[1:] = [m for m in messages[1:] if m.string and (use_fuzzy or not m.fuzzy)] messages.sort() - ids = strs = b'' + ids = bytearray() + strs = bytearray() offsets = [] for message in messages: diff --git a/tests/messages/test_mofile.py b/tests/messages/test_mofile.py index e572931c2..84b6f431d 100644 --- a/tests/messages/test_mofile.py +++ b/tests/messages/test_mofile.py @@ -11,8 +11,11 @@ # history and logs, available at https://github.com/python-babel/babel/commits/master/. import os +import struct from io import BytesIO +import pytest + from babel.messages import Catalog, mofile from babel.support import Translations @@ -104,3 +107,54 @@ def test_read_mo_decodes_message_context() -> None: assert message.context == 'ほげ' assert message.string == 'åäöÅÄÖ' assert not catalog.get('x') # Not without the context + + +@pytest.mark.parametrize('charset', ['utf-8', 'iso-8859-1']) +@pytest.mark.parametrize('use_fuzzy', [False, True]) +def test_write_mo_string_tables(charset, use_fuzzy): + catalog = Catalog(locale='fr', charset=charset) + catalog.add('Coffee', 'Café') + catalog.add(('One file', 'Many files'), ('Un fichier', 'Des fichiers')) + catalog.add('Open', 'Ouvrir', context='menu') + catalog.add('Draft', 'Brouillon', flags=['fuzzy']) + catalog.add('Untranslated', '') + output = BytesIO() + + mofile.write_mo(output, catalog, use_fuzzy=use_fuzzy) + + data = output.getvalue() + _, _, count, key_table, value_table, _, _ = struct.unpack_from('Iiiiiii', data) + entries = {} + for index in range(count): + key_size, key_offset = struct.unpack_from('ii', data, key_table + 8 * index) + value_size, value_offset = struct.unpack_from('ii', data, value_table + 8 * index) + assert data[key_offset + key_size] == 0 + assert data[value_offset + value_size] == 0 + key = data[key_offset:key_offset + key_size].decode(charset) + value = data[value_offset:value_offset + value_size].decode(charset) + entries[key] = value + del entries[''] + expected = { + 'Coffee': 'Café', + 'One file\x00Many files': 'Un fichier\x00Des fichiers', + 'menu\x04Open': 'Ouvrir', + } + if use_fuzzy: + expected['Draft'] = 'Brouillon' + assert entries == expected + + +def test_write_mo_writes_bytes_once(): + catalog = Catalog() + catalog.add('Message', 'Translation') + writes = [] + + class Output: + def write(self, data): + assert type(data) is bytes + writes.append(data) + + mofile.write_mo(Output(), catalog) + + assert len(writes) == 1 + assert mofile.read_mo(BytesIO(writes[0]))['Message'].string == 'Translation'