Skip to content

Count the keyword when wrapping PO values - #1420

Open
ryanchou1994 wants to merge 1 commit into
python-babel:masterfrom
ryanchou1994:fix/po-width-keyword
Open

ryanchou1994 wants to merge 1 commit into
python-babel:masterfrom
ryanchou1994:fix/po-width-keyword

Conversation

@ryanchou1994

Copy link
Copy Markdown

Fixes #1266.

Problem

write_po() puts each value on the same line as its keyword (msgstr "..."), but normalize() only compared the quoted string plus the line prefix against width. The keyword itself was never counted. With the 116-character msgstr from the issue and pybabel update -w 120, the quoted string is 118 characters, so it was treated as fitting and written as a single 125-character line (and an already wrapped value was un-wrapped).

Change

  • normalize() gets an optional first_line_offset argument (default 0, so existing callers get the same output as before).
  • When a single-line value fits on its own line but not after its keyword, it is written in the multi-line form (msgstr "" followed by the value on the next line), the same layout already used for wrapped values.
  • A value that is too long even on its own line (e.g. one long word) keeps the old output, since moving it would not help.
  • _format_message passes the keyword length through a small _format_field helper, so msgctxt, msgid, msgid_plural, msgstr, msgstr[N] and obsolete (#~ ) entries are all covered.

Note that this changes output for values whose length falls in the last few columns before width (counting the keyword): they now start on their own line. Running pybabel update on an existing catalog will reflow those entries once.

Not changed on purpose: the conflict writer _format_conflict (used by concat/merge), and #| msgid previous-id comments.

Tests

  • test_wrap_long_msgstr_including_keyword: the case from the issue at width=120.
  • test_wrap_keyword_lines_within_width: context, plural and obsolete entries at width=40, asserting no line exceeds the width.

Both fail on master (ecf1da8) and pass with this change. Full run on Linux (Python 3.14, CLDR 48.2 imported with scripts/download_import_cldr.py): pytest tests babel/messages/pofile.py --doctest-modules goes from 6618 to 6620 passed, no failures.

write_po() decided whether a value fits on one line without the
keyword written before it (msgid, msgstr, msgstr[N], msgctxt), so
a value that fit only on its own line was written as a single
over-long line. With width=120 the example in the issue produced a
125-character msgstr line.

normalize() gets an optional first_line_offset argument. When a
single-line value fits by itself but not after the keyword, it is
written in the multi-line form starting with an empty string.
Values that cannot fit even on their own line keep the old output,
and callers that don't pass the argument are unchanged.

Fixes python-babel#1266
Copilot AI balanced review requested due to automatic review settings November 2, 2026 05:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Over-long msgstr not line wrapped when setting --width

2 participants