Skip to content

Documents in a language the engine does not build in

Two languages ship inside the binary: ja-JP and en-US. Everything else is a file. Point the engine at one YAML file under packs/locale/ and your documents come out in that language. The build does not change.

Locale data compiled into the engine travels with every copy of it, and a service that only prints Taiwanese invoices has no reason to carry Hindi month names. Japanese and English are the exception on purpose. Because they are compiled in, the engine can render without reading a single file, and that is what lets the WASM bundle work in a browser with nothing else fetched.

What a pack decides

One pack carries the values a reader of the document actually sees.

  • Currency: the default code, its symbol, its name in that language, and which side of the number the symbol goes
  • Dates: patterns like d MMM y, plus month and weekday names
  • Numbers: the group and decimal separators, and how digits are grouped
  • Units: the word attached to a quantity, so 3 becomes 3 รายการ
  • Fonts: which font pack has the glyphs, and which one supplies the Latin they lack

Nothing in the template names a language. defaults.locale takes a tag, and the pack decides what the values look like. The receipts in the Gallery are the same layout at the same coordinates, six times over. What differs between them is the locale tag and the labels written in that language.

Loading one

Add a line to the template.

yaml
defaults:
  locale: zh-TW
  currency: TWD

Then let the engine find the file. On the command line that is --locale-dir, or $SHOJIKU_LOCALE_DIR, or ./packs/locale if you set neither.

bash
shojiku render --templates templates.yml --params params.json \
  --locale-dir ./packs/locale --output out.pdf

In a browser or a Workers isolate there is no file to read, so you hand the engine the pack's text directly: setLocale(tag, text).

A tag with neither a pack nor a builtin does not quietly fall back to another language. The render fails, and the error lists the builtin tags and every directory it searched.

The packs that ship

LocaleCurrencyDateFonts
zh-TWTWDy年M月d日Noto Sans TC
zh-CNCNYy年M月d日Noto Sans SC
hi-ININRd MMM yNoto Sans Devanagari + Noto Sans
fil-PHPHPMMM d, yNoto Sans
th-THTHBd MMM yNoto Sans Thai + Noto Sans

Fonts work the same way: the pack names them, and nothing scans your system. The Devanagari and Thai faces carry no Latin at all, so those two packs list noto-sans behind them for it. Filipino uses the Latin script, so its pack ships no font of its own.

Where the typesetting itself differs

A pack is data, so adding one does not change how anything is laid out. Three languages are the exception, and each is handled inside the engine.

Chinese line-break prohibition. A closing bracket or a full stop may not start a line. When one lands there, the character before it is moved down as well, so the pair stays together at the head of the new line. The same characters are held back in Chinese and in Japanese, and lineBreak selects how strict the rule is.

Indian digit grouping. ₹1,23,45,678 groups three digits from the right and then two at a time. The widths are numbers the pack supplies, so the grouping code takes no locale argument at all.

Thai word segmentation and the Buddhist era. Thai is written without spaces between words, so a segmenter decides where a line may break (details). Dates print in the Buddhist era: 2026 comes out as 2569. For the Gregorian year, ask the date field for its gregorian variant.

Adding a language

Adding a locale requires no engine change. Write one entry in PACK_CONFIG in scripts/gen-locale-builtins.py and run the script; the values come from CLDR. If that entry names a font pack the repository does not have yet, building the font pack is separate work.

About the wording

Nobody who worked on this page reads Thai or Chinese. The numbers and formats come straight from CLDR, but the word choices have not been checked by anyone who would notice if they read strangely on a real document. If something is wrong, please open an issue.