The call-site translation boundary for non-command strings (dialog prose, chrome
tooltips, statusbar text). Packs overlay by key:
label: t('formulaBar.cancel')
Command/panel chrome does NOT use this — it resolves by convention at the registries.
In React, do not import this — use useTranslator() (@sheetxl/utils-react), which returns
a t bound to a language subscription. This function reads a singleton, so React cannot know
its value changed; a component calling it directly renders the string ONCE and silently freezes
it at whatever language was active on mount unless that component separately subscribes. The
export exists for non-React callers, and scripts/i18n-chrome-lint.mjs fails any .tsx that
imports it.
The English never appears here — it lives only in the package's locales/en/messages.json
(I18N-DESIGN.md decision 9). The key is the identity; a second copy of the sentence at the call
site tells the next reader nothing and ships the same bytes twice.
t('error.named.nameEmpty') // no parameters t('error.named.nameExists', { name }) // with parameters
A miss renders the key: visible and debuggable, never blank. That is also what happens where no
pack can load at all (Node, the CLI) — those contexts need the English pack registered from disk
rather than an inline copy at every call site.
The call-site translation boundary for non-command strings (dialog prose, chrome tooltips, statusbar text). Packs overlay by key:
Command/panel chrome does NOT use this — it resolves by convention at the registries.
In React, do not import this — use
useTranslator()(@sheetxl/utils-react), which returns atbound to a language subscription. This function reads a singleton, so React cannot know its value changed; a component calling it directly renders the string ONCE and silently freezes it at whatever language was active on mount unless that component separately subscribes. The export exists for non-React callers, andscripts/i18n-chrome-lint.mjsfails any.tsxthat imports it.The English never appears here — it lives only in the package's
locales/en/messages.json(I18N-DESIGN.md decision 9). The key is the identity; a second copy of the sentence at the call site tells the next reader nothing and ships the same bytes twice.A miss renders the key: visible and debuggable, never blank. That is also what happens where no pack can load at all (Node, the CLI) — those contexts need the English pack registered from disk rather than an inline copy at every call site.