Formatting and locale / Language in generated text
Translated labels that no longer fit the column they were sized for
The same label in another language is frequently longer, and a layout tuned to the original wraps, clips or reflows once the translation goes in.
The defect
Labels are the shortest strings in a document and therefore the ones given the least room: a column header, a field name, a button, a total row. Translations of short strings are often proportionally much longer than translations of long ones, because a short label has no slack to absorb a difference in how a language expresses the idea. So the strings with the least space are the ones that grow the most. In a document this does not merely look untidy: a header that wraps to two lines changes the height of the header row, which changes how many rows fit on the page, which changes the pagination of the whole document.
What it does to the document
This is the localisation problem with the largest layout consequence, because it changes the height of a repeating element. A header row one line taller removes a row from every page, which on a long document adds pages, which moves everything that was positioned relative to a page.
The decisions
Reasons rather than a description of the code. Formatting rules are data that changes, so most of these are about taking the rule from a maintained source rather than encoding today's answer.
- Size columns for the longest translation you actually ship, not for the source language, and measure rather than estimating. Any single expansion figure you have read is a rule of thumb, not a measurement of your strings.
- Give every label the room to wrap to two lines and reserve that height on every language, so the layout does not change shape between them.
- Do not solve it by shrinking the type for one language. A document set two points smaller in one market is a different document.
- Avoid abbreviating in the translation to make it fit. The abbreviation conventions are not yours to invent and the result is frequently unreadable to a native reader.
- Re-check pagination after a new language is added, not just layout. The interesting failures are downstream of the header row growing.
- Where a label genuinely cannot fit, change the design rather than the string: move the label above the column, or use a legend.
In code
The formatting happens before the markup exists, so this is mostly code rather than CSS. The wrong version is kept in a comment where the wrong version is the thing people write.
/* Reserve the height a two-line label needs on every language, so the
header row is the same height in all of them and the pagination does
not change between markets. */
.items thead th {
min-height: 2.6em; /* room for two lines, always */
vertical-align: bottom;
line-height: 1.2;
hyphens: none; /* do not invent hyphenation in a label */
}
/* Size the columns for the longest translation shipped, measured rather
than estimated. Keep the numeric columns fixed so only the text column
absorbs the difference. */
.items .desc { width: 52%; }
.items .num { width: 16%; }
/* Not this: a per-language type size, which makes one market's document
a different document.
:root[lang="de"] .items { font-size: 8pt; } */What people do instead
Testing the layout in the source language and treating translation as a content task that happens afterwards. By then the column widths are settled and the only lever left is shrinking type or abbreviating, which are both bad answers to a problem that was cheap to avoid.
How to find out rather than assume
Render the full document in every language you ship, and compare the page counts. If they differ, something is reflowing. Then look at every column header in the longest language and confirm none has wrapped past the space reserved for it.
Where the API sits in this
Nowhere, and that is worth saying plainly. This API renders HTML to PDF. By the time markup reaches it, every value in it is already a string that somebody's code produced, so there is no locale option on the render because there would be nothing for one to do. What the render controls is the page: the size, the margins, and whether the formatted value still fits the column it landed in. That is why each of these pages has a paragraph about the layout consequence, because that is the half a document adds to the general problem.
Frequently asked
Should the document use the sender's conventions or the reader's?
That is a decision rather than a fact, and the only wrong answer is not making it. The issuer's conventions are defensible for a document that is a record of what the issuer did. The recipient's are defensible for a document meant to be acted on. What fails is a document that mixes them, because a reader who sees two conventions on one page cannot tell which one any given figure is in.
Is it enough to set the document language?
No. A language attribute helps with hyphenation, with the assistive technology reading order and with anything that needs to know what language the text is in, and it changes none of the values. A date formatted as 03/04/2026 is exactly as ambiguous in a document declared as English as in one declared as anything else. The values are formatted upstream.
Why does this belong in a document reference rather than a general one?
Because a document cannot be re-rendered by its reader. On a screen a wrong format is annoying and a user can often change a setting. On paper the format is final, the layout was tuned to the string lengths of one language, and a value that grew by a few characters can push a table onto another page. The formatting problem and the pagination problem are the same problem here.
Related formatting topics
The defects that travel together, then the rest of the same group.
Plurals in generated line text, and 1 items
Text assembled around a number, where the grammar depends on the number and the template usually assumes there are exactly two possibilities.
Units on a document, and the quantity with no unit at all
A measured quantity printed without its unit, in the wrong system for the reader, or separated from its unit by a line break.
Why 03/04/2026 is a defect in a document that crosses a border
An all-numeric date whose day and month can be swapped without producing anything that looks wrong, which makes it unreadable rather than merely unclear.
Numbers and codes inside right-to-left text, in the wrong order
A left-to-right run such as an amount, a reference or a URL embedded in right-to-left text, where the boundary between the two is resolved by an algorithm and not always the way you meant.
Alphabetical order that is not alphabetical to the reader
The order a list is sorted into, which is defined by the language rather than by the byte values of the characters.
Line-item table, as a layout problem
The block this value lands in, and what it takes to make it fit.
Every formatting and locale topic
The full list, grouped by money, time, identity and generated language.
Writing systems in a PDF
The other half: whether the glyphs of a script come out shaped and joined correctly.
Values are formatted by your code before they reach the markup, so every fix on this page is upstream of the render. What the render decides is whether the result still fits.