i18n: keep messages.pot location comments on one line to prevent merge conflicts#12903
i18n: keep messages.pot location comments on one line to prevent merge conflicts#12903lokesh wants to merge 8 commits into
Conversation
Babel wraps the #: location comments at 76 chars, so adding or removing one file from a string's location list re-flows the whole block and rewrites filenames that didn't change. That turns unrelated PRs into overlapping diffs and causes spurious messages.pot merge conflicts (internetarchive#12837). The issue proposed write_po(..., width=0), but Babel mirrors xgettext and always wraps comments regardless of width, so width=0 only unwraps msgid text (the downside) without unwrapping the location comments (the actual fix). Instead, post-process write_po's output to collapse each #: block onto a single line. Adding a file now just extends that line; message text stays wrapped and readable. This first regeneration reflows the whole file as a one-time change. Closes internetarchive#12837
|
Important: Are there other types of comments (like fuzzy) that might break? |
…t-disable-line-wrapping # Conflicts: # openlibrary/i18n/messages.pot
…t-disable-line-wrapping
Replace the _unwrap_location_comments post-processing pass with write_po(..., width=POT_WIDTH). A large width disables Babel's line wrapping entirely, so each #: location comment stays on a single line — the same conflict fix, with no custom string-munging code to maintain. This also single-lines the msgid/msgstr text. For a generated template that is cosmetic and, if anything, more merge-friendly: a string's text only changes when the English itself changes (rare and localized), and a one-line msgid can't reflow its neighbours either. width=0 still does not work here (Babel mirrors xgettext and always wraps comments at 76 regardless), so a large width is the way to turn wrapping off; the reasoning is captured in a comment by POT_WIDTH. Tests now assert the real generation path (write_po at POT_WIDTH) keeps locations on one line and leaves flags/message text intact, replacing the unit tests for the deleted helper.
|
Good question — and digging into it is actually what led me to simplify the whole PR. Short answer: no, nothing else breaks.
I've replaced |
|
@cdrini heads-up — I checked this against your JS i18n experiment (#12968) and there's no directional conflict: that PR changes what gets extracted, this one only changes how the If anything this helps #12968: adding JS extraction appends new files to lots of existing strings' location lists — exactly the reflow churn this PR eliminates. |
…t-disable-line-wrapping # Conflicts: # openlibrary/i18n/messages.pot
for more information, see https://pre-commit.ci

Closes #12837
Refactor. Stops
messages.potfrom generating spurious merge conflicts between unrelated PRs.The problem, in plain terms
openlibrary/i18n/messages.potis a generated file we commit. Every translatable string carries a#:"location comment" listing the files that use it:Babel wraps those lines at 76 characters. So when a PR adds one file to a string, the line tips over the limit and Babel re-flows the whole block — rewriting filenames that didn't actually change:
subjects.htmlandwork_search.htmlgot pushed onto a new line even though nothing about them changed. Now if a second in-flight PR also touches one of those templates, both branches have rewritten the same physical lines → git reports a conflict, even though the two PRs added completely unrelated files. The wrapping is what turns a non-overlapping change into an overlapping one.What this PR does
Write the file with a very large
width, which disables Babel's line wrapping. Each string's#:location comment now stays on a single line, so adding a file just extends that one line:Unrelated string changes no longer share any lines, so they stop conflicting.
That's the whole change. No post-processing pass, no custom string-munging to maintain.
Why not
width=0The issue suggested
write_po(..., width=0). It does the opposite of what we want. Babel deliberately copiesxgettext's behaviour — it always wraps comments, even when wrapping is otherwise off. From Babel's source:So
width=0leaves the#:location comments wrapped at 76 (the actual problem — untouched) and only unwraps themsgid/msgstrtext. A large positive width, on the other hand, raisescomment_widthtoo — so the locations finally stop wrapping. That's the lever.About the message text
A large width also single-lines the
msgid/msgstrtext (Babel's onewidthknob governs both). For a generated template that's a wash, and arguably better:msgidcan't reflow its neighbours either, so it carries the same conflict-resistance the locations now have.For developers
messages.potconflicting again?" when rebasing or merging concurrent PRs that touch templates.which templates use this string) are kept — we didn't drop them to solve this..pot-touching PRs are mid-flight.For translators
.pothas the exact same set ofmsgids and the exact same locations; only line wrapping differs. No string was added, removed, or altered.#:"where is this string used" hints translators rely on for context are preserved.Technical
openlibrary/i18n/__init__.py—extract_messagesnow callswrite_po(f, catalog, include_lineno=False, width=POT_WIDTH)wherePOT_WIDTH = 1_000_000(a comment by the constant records whywidth=0doesn't work and why a large width does). The previous_unwrap_location_commentshelper and its buffer round-trip are removed.#,flags (e.g.#, fuzzy,#, python-format),#.translator notes,#|previous-msgid — are written by Babel exactly as before; nothing custom touches them. (fuzzyin particular can't appear in a.pottemplate anyway — it's a flag on a translation in.pofiles.)Testing
openlibrary/tests/core/test_i18n.py(Test_pot_width): generate a catalog through the realwrite_po(..., width=POT_WIDTH)path and assert a multi-file string's locations stay on a single#:line, and that flags (#, python-format) and message text survive untouched.messages.potwith the actual extraction script and verified viaread_pothat it round-trips to an identical set of msgids and per-string locations vs. the previous version (semantic no-op): 1953 msgids, all locations identical.#:continuation lines remain in the regenerated file.rufflint + format pass on the changed Python files.Possible follow-ups (out of scope)
#:line per file would make even two PRs editing the same string's location list merge cleanly. It's the most conflict-proof but unconventional and makes the file taller; this PR keeps the conventional space-joined single line..gitattributesmerge driver that regeneratesmessages.poton conflict, or having CI own the file outright (both noted in the issue).Stakeholders
@cdrini (i18n lead)