Documentation

Pro handbook

Language

Everything the libraries say to a user — rejection messages, tool labels in defaultToolbarEntries(), action hints, measurement labels — comes from message tables. English is built in. Every other language is a table you register yourself; the packages register nothing when they are imported, so you only ship the languages you use.

Programs should not read these texts: every user-visible rejection also carries a stable code, and every edit a stable label (see Events).

German, French, Spanish, Dutch#

@vectorstream/core, @vectorstream/editor and @vectorstream/adapter each ship a German table (coreDe/editorDe/adapterDe) and, as of this version, a French, Spanish and Dutch one (coreFr/editorFr/adapterFr, coreEs/editorEs/adapterEs, coreNl/editorNl/adapterNl) — all four follow exactly the same pattern, shown here for German. Register the three tables of one language and switch the page locale — once, before anything renders text:

import { adapterDe, adapterMessages } from '@vectorstream/adapter';
import { coreDe, coreMessages, registerLocale, setLocale } from '@vectorstream/core';
import { editorDe, editorMessages } from '@vectorstream/editor';

/**
 * German for every library message. The packages ship English only and register nothing on
 * import: you register the tables you want, once, before anything renders text.
 */
export function useGerman(): void {
  registerLocale('de', coreMessages, coreDe);
  registerLocale('de', editorMessages, editorDe);
  registerLocale('de', adapterMessages, adapterDe);
  setLocale('de');
}

French, Spanish and Dutch work the same way — swap the locale tag ('fr'/'es'/'nl') and the three table imports for the language's own (coreFr/editorFr/adapterFr, and so on).

French, Spanish and Dutch are machine-translated and have not been reviewed by a native speaker. This is a deliberate choice (short draft gap 7, user decision of 12.10.2026, risk knowingly accepted rather than waiting for a review that may never happen) — not a reason to treat them as less complete: defineTranslation checks every one of the three the same way it checks German, so no key, placeholder or plural form is missing or mismatched. What a compiler cannot check is wording — if something reads oddly in one of the three, that is expected until someone who speaks the language looks it over; corrections are welcome.

The map packages have no message tables of their own. The maps' built-in controls (zoom buttons, attribution) are the map libraries' business; the application in this repository translates them through the maps' own options.

How it works#

  • One locale per page. setLocale(tag) switches every table at once; getLocale() returns the current tag ('en' by default). Library code throws without access to an editor instance, so a per-editor language could not reach those messages.
  • Lookup goes from the exact tag ('de-AT') to its base language ('de') to English. A table may therefore be partial: every key it lacks reads in English.
  • Texts are produced when they are created. An error's message and a toolbar entry's label are rendered in the locale current at that moment and are not rewritten later. After switching the language, rebuild what shows text — the application in this repository reloads the page.
  • Numbers follow the locale too: formatNumber(1234.5) gives 1,234.5 in English and 1.234,5 in German. The measurement values of the map adapters use it.
  • Any well-formed BCP 47 tag is accepted; without a registered table the texts read in English while numbers still follow the tag. A malformed tag throws a RangeError.

Codes and parameters#

A user-visible rejection carries:

  • code — namespaced and stable, e.g. 'editor.layerActiveHide' or 'core.importNoGeometry';
  • params — the values for the message's placeholders, plus IDs of the elements concerned;
  • message (on an error) or detail (on a Violation) — the rendered sentence.

formatMessage(code, params) renders any code again in the current locale — for example to show a logged rejection in the user's language. isCodedError(e) tells whether an error carries a code. Internal programming errors — a plain TopologyError, StructuralError, RollbackError — are English only and have no code.

Your own translation#

A table for another language — one of the four above is already shipped, so this is for a fifth one — or your own wording for some keys:

import { registerLocale, setLocale } from '@vectorstream/core';
import { editorMessages } from '@vectorstream/editor';

/**
 * Your own language for some editor texts. A table may be partial: every key it leaves out reads
 * in English. The compiler checks the keys; keep the placeholders (`{name}`) of the English text.
 */
export function useItalian(): void {
  registerLocale('it', editorMessages, {
    toolDraw: 'Disegna area',
    toolRectangle: 'Rettangolo',
    layerActiveHide: 'Il livello «{name}» è attivo e non può essere nascosto.',
  });
  setLocale('it');
}

registerLocale(tag, messages, table) merges into what is already registered for that tag and package; later calls override single keys. The compiler checks that every key exists in the package's English table. For a complete table, defineTranslation(englishSource, table) also checks that no key is missing and that each text uses the same placeholders as the English one — the packages' German tables are built that way. A plural entry is an object of plural categories (one, other, …) selected by the parameter n.

The message sets are exported per package: coreMessages / coreDe / coreFr / coreEs / coreNl, editorMessages / editorDe / editorFr / editorEs / editorNl, adapterMessages / adapterDe / adapterFr / adapterEs / adapterNl. Each has t(key, params) to render one of its texts and code(key) for the code.

The tests of both examples check: English by default; after useGerman() the same rejection has the same code and parameters but a German message, formatMessage renders the old English error in German and back; numbers switch format; a partial Italian table translates the given tool labels and leaves the rest English; 'it-CH' falls back to 'it'; and an unknown key does not compile.

API: setLocale, registerLocale, formatMessage, defineTranslation.

See it on a map.