Formatting and locale / Language in generated text
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.
The defect
Three failures, in increasing order of how often they happen. A quantity printed with no unit at all, which is common in a column headed with a unit that then gets abbreviated away or repeated inconsistently. A quantity printed in a system the reader does not use, which they will either convert wrongly or not at all. And a quantity separated from its unit by a wrap, which turns one value into two tokens and is the reason a figure gets read as the wrong magnitude. All three are worse in a document than on a screen, because a document is often the record used to check a physical thing against.
What it does to the document
The unit is what makes a narrow numeric column overflow, which is why it gets dropped from the rows first. Putting it in the header solves the width problem and the consistency problem at once, and it is the reason the quantity-and-unit column page recommends separate cells rather than a concatenated string.
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.
- Print the unit once in the column header and never on the rows, or on every row and never in the header. Both work; mixing them is what produces rows with no unit.
- Bind the value to its unit with a non-breaking space so they cannot be separated by a wrap.
- Format with a library that takes a locale and a unit, so the abbreviation and its placement come from data rather than from a template.
- Where the document crosses systems, give the converted value its own column rather than putting it in brackets, and round it to the precision of the source rather than to the machine's.
- Use the unit the measurement was taken in as the primary, and treat anything else as derived, so the authoritative figure is the one that was measured.
- Do not abbreviate to a symbol that is ambiguous in the destination. A unit that means two things is worse than a longer name.
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.
const qty = 1250;
// Locale-aware unit formatting: the abbreviation and its placement come
// from data rather than from a template.
new Intl.NumberFormat("en-GB", {
style: "unit",
unit: "kilogram",
unitDisplay: "short",
}).format(qty);
/* Bind the value to its unit so a wrap cannot separate them:
<td class="qty">1,250 kg</td>
or, preferred in a column, the unit in the header only:
<th class="num">Weight (kg)</th>
<td class="num">1,250</td>
.num { white-space: nowrap; font-variant-numeric: tabular-nums; } */What people do instead
Putting the converted value in brackets after the original. The column now contains two numbers of different lengths per row, the alignment is gone, and the bracketed figure reads as an aside rather than as a measurement.
How to find out rather than assume
Look for any quantity on the rendered page that has no unit within reading distance of it. Then check the widest row in the column and confirm the value and unit did not wrap apart. Then confirm the converted values, if any, are rounded to the precision of the source.
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.
Where the half cent goes, and why the document disagrees with itself
Which way a value exactly halfway between two representable amounts is rounded, and at which point in the calculation the rounding happens.
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.
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.
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.
Quantity and unit columns, 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.