0021. Languages and Chinese printing
- Status: Accepted
- Date: 2026-10-28
Context
Section titled “Context”Malaysian shops work in English, Malay and Chinese, often all three in one shop: a cashier
reads Malay, the kitchen reads Chinese, and customers want receipts they can read. M8 adds
Malay (ms) and Simplified Chinese (zh-Hans) to the POS app, the back office and the printouts.
Each reader needs their own language, and switching must not need a restart.
Thermal printers mostly print only Latin text in their built-in fonts. Models made for the Chinese market also have a Chinese font (GB2312/GBK, read in their “Chinese mode”), but many printers sold in Malaysia don’t. Printing Chinese must work on both, without native code that can only be checked on a device.
Decision
Section titled “Decision”Who chooses which language.
- Device screens: each POS device chooses its own screen language. It is stored on the device, offered on the setup screen, the PIN pad and in Settings → Language, and starts from the system language.
- Back office: each browser chooses its own language. It is stored in the browser and starts from the browser’s language.
- Shop settings: the shop sets a receipt language, an optional second receipt language, and a kitchen ticket language. The kitchen ticket language defaults to the receipt language. These settings sync like the other shop settings.
- Z-reports and test pages: these are for staff, so they print in the device’s screen language.
Switching without a restart.
- POS app: the provider holds the translator, so every screen re-renders when it changes.
- Back office: the router remounts with the new language (the data cache stays), and
<html lang>follows. - Hub errors: the hub answers in the language a request asks for (
Accept-Language). The back office and the app send theirs, so errors appear in the reader’s language. Handlers send catalog keys (fail("server.…")), and one hook turns them into words.
Two languages on a receipt. With a second language, labels print in both (“Total / Jumlah”). A label followed by one value prints in both, with the value once (“Receipt / Resit K1-0042”, “Served by / Dilayan oleh Aminah”; changed 2026-10-28, after the third Mac pass found those lines in one language). Messages with several values (tax lines) print in the first language only, so numbers aren’t printed twice. Lines that grow too long wrap. Item names are the shop’s own data and print as entered.
Catalogs.
- Layout: every language has the same keys, split into the POS app, printing, the back office and the hub’s messages.
- Tests: they check that each language has every key with the same placeholders.
- No hard-coded text: a test scans the POS app’s and back office’s components for text
written into JSX or into props people read (
label,title,placeholder,aria-label…).
Chinese printing. Each printer chooses one of two modes:
- Image (the default): works on every printer that prints images (
GS v 0), which is all the printers we target. A line containing Chinese is drawn as a 1-bit image at the printer’s own cell size (12 × 24 dots, wide characters 24 × 24) and sent like a QR image, so columns line up with lines the printer draws itself. The glyphs are GNU Unifont’s 16-dot bitmaps (SIL OFL 1.1), scaled 1.5× with smoothing. - Printer’s own font: for printers with a Chinese font. The line is sent in Chinese mode
(
FS &) with GBK bytes, then the job leaves Chinese mode again (FS .).
What the font covers. GBK: simplified and traditional Chinese, which includes GB2312 and is read by GB18030 printers. GB2312 alone would miss characters common on Malaysian menus, such as 粿 in 炒粿条 and 叻 in 叻沙. The glyphs and the GBK table are packed into one generated module of about 580 KB (deflated, base64). It is unpacked into flat arrays the first time Chinese is printed.
Consequences
Section titled “Consequences”- Printing:
- Every printer can print Chinese out of the box, with no native code and no new permissions.
- Image lines print a little slower than text and look pixelated next to the printer’s own font. Shops with a Chinese printer can switch that printer to its own font.
- Characters outside GBK print as “?”. Emoji are among them.
- Unifont’s Latin glyphs, used in image lines, don’t match the printer’s own font exactly.
- Size:
- The POS app’s JavaScript grows by about 580 KB for the font.
- The back office grows by about 70 KB for the extra languages, now one 510 KB file (150 KB gzipped).
- Translations:
- Plugin labels already carry
en,msandzh-Hans(ADR 0018). They now follow the screen’s language on screens and the receipt language on receipts. - Sample menu items, staff names and other shop data are not translated.
- Adding a language means a catalog, a tag in
LOCALES, and a review of receipt widths (labels must fit a 32-column line).
- Plugin labels already carry
- Hub API: catalog keys for errors are part of how the hub talks. Plugins still send their own words.
For AI tools: llms.txt ·llms-full.txtopenkedai.com