Translations: the .po reader turns a literal backslash before n or t into a newline or tab, losing the entry #498
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#498
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, by round-tripping strings through the snapshot'sdumpandparse.Checked again on 2026-10-01 against
mainat0a5cad587: still present._unquotedecodes escapes with chainedstr.replacecalls:\nfirst and\\last. A string that_quotewrote with an escaped backslash followed bynortis therefore read back wrong. Separately,_quotedoes not escape a carriage return, so it is written raw, andparsesplits it as a line ending. Either way the entry comes back under a different msgid than the one written, and its translation no longer matches the string in the source.Where:
core/messages_tool.py:524-528(_unquote:.replace("\\n", "\n").replace("\\t", "\t").replace('\\"', '"').replace("\\\\", "\\"));core/messages_tool.py:391-395(_quote, which escapes\\,",\nand\tonly);core/messages_tool.py:491(for raw in text.splitlines()inparse).What happens: with the snapshot's functions:
_unquote(_quote("C:\\new folder"))returnsC:\followed by a newline andew folder;_unquote(_quote("tab\\t"))returnstab\followed by a tab;Type C:\newthat goes throughdumpand thenparsecomes back with a msgid of backslash, newline,ew, so the original entry is lost;On the next
extract, the existing entry no longer matches the extracted string, so it is dropped with its translation.extract --checkthen reports the catalogue as stale, and extracting again drops the translation again. The.mocompiled from the parsed catalogue carries the corrupted key, so the runtime does not find the translation either.Why it matters: no message in today's catalogues contains a backslash, so nothing is lost yet. The first help text that mentions a Windows path, a regular expression or an escape sequence would lose its translation in every language on every run. The tool is also published as the
postulo-messagescommand (pyproject.toml:86) for plugin repositories, whose strings Postulo does not control.Fix: decode in one left-to-right pass,
re.sub(r'\\(.)', lambda m: {"n": "\n", "t": "\t", "r": "\r", '"': '"', "\\": "\\"}.get(m.group(1), m.group(0)), chunk), and have_quoteescape\ras\\r.Done when
tests/test_messages_tool.pyround-trips, throughdumpandparse, a catalogue whose msgid and msgstr contain a literal backslash beforen,tand", and a carriage return. It asserts every entry comes back unchanged. Today the backslash and carriage-return cases fail.