Saltar al contenido
MellowCAD

Esta sección solo está escrita en inglés.

Conceptos

En npm

Languages

Spanish and Arabic, rewording a few words, and a language of your own: the interface and the command language.

The engine speaks English, Spanish and Arabic, and a host can reword any of it or add a language of its own. A language is passed to the engine, never registered globally, so one page can run two engines in two languages.

Two languages in one

As in AutoCAD's language packs, a language has two parts:

  • The interface (interface): what a host shows around the drawing — tool labels, palette rows, the names of snaps, colours, ByLayer, views and the ViewCube's faces.
  • The command language (commands): command names and aliases, prompts, keywords, command-line messages, and everything drawn on the canvas.
LanguageInterfaceCommand language
EnglishBuilt inBuilt in, the global one
SpanishesSpanish AutoCAD's: LINEA, DESFASE, [Cerrar/desHacer]
ArabicarThe global one, as there is no Arabic AutoCAD

In every language, _ before a global name or keyword reaches the global vocabulary — _LINE, _C — as AutoCAD's scripts and macros rely on. A keyword whose global id has a space is typed as one word there (_LINESPACING for MTEXT's Line spacing).

Commands are always written left to right, also on an Arabic page: state.commandLine.lang is the command language's BCP 47 tag, for the lang attribute of whatever shows the prompts.

Choosing one

import { CadDriver, CadEngine, commandCatalog } from "@mellowcad/engine"import { ar } from "@mellowcad/engine/locales/ar"import { es } from "@mellowcad/engine/locales/es"
const engine = new CadEngine(canvas, { locale: es })engine.setLocale(ar) // rewords the state and an open prompt; the history stays as written
new CadDriver({ locale: es }) // headlesscommandCatalog(es) // every command's name, aliases and description, in Spanish

recordDemo(script, { locale }) and engineText(locale) take one too. Import a language only where it is used, or load it on demand with import("@mellowcad/engine/locales/es"): English needs no import.

Rewording a few words

extendLocale lays your words over a language, key by key, and returns a new language:

import compile from "icu-minify/compile" // at build time, never in the page's bundleimport { extendLocale } from "@mellowcad/engine"import { es } from "@mellowcad/engine/locales/es"
const spanish = extendLocale(es, {  interface: { tool: { line: "Línea recta" } },  commands: {    messages: { unknownCommand: compile('No existe el comando "{name}".') },  },})

What you give wins, everything else is the base's, and the base is untouched. A list of names is replaced whole. To reword English, start from en (@mellowcad/engine/locales/en).

Giving commands to a language without a command language (Arabic) gives it one, with the rules below: a command it does not name loses its aliases.

A language of your own

A language is an EngineLocale:

interface EngineLocale {  tag: string // BCP 47, for plurals; digits stay latin  interface: DeepPartial<InterfaceCatalog>  commands?: DeepPartial<CommandCatalog>}

English's catalog, locales/en.json, is the reference: its keys are every word the engine says, and its type is the type of every other language.

  • interface may leave out any key. That key is shown in English, and only that key.
  • commands is complete or absent. Without it, the command language is the global one: English's names, aliases, prompts and keywords. With it, the language should name everything, because what it leaves out degrades:
    • a command it does not name is reached by its global name alone (ERASE, never E): English's aliases are English's, and another language gives the same letters to other commands (U is JOIN's in Spanish);
    • a keyword it does not name is its global word (Close);
    • any other message is English's.
  • names maps a global name to the language's name and then its aliases ("OFFSET": ["DESFASE", "DF", "EQ"]). No two commands may share one.
  • keywords gives each command's keywords by their global id. The letters a user types are the capitals anywhere in the word (desHacer → H), as AutoCAD's initget reads them. A keyword is typed and confirmed with Enter or Space, never run on a letter alone.
  • keywordLabels gives a keyword the gloss AutoCAD lists it with (Ttr (tan tan radius)), or the form it lists a keyword declared otherwise in (TL for TEXT's TLeft). A language labels only what English labels.
  • promptKeywords spells a keyword differently at one prompt, by the prompt's id: each AutoCAD prompt declares its own, so Spanish FILLET lists [RAdio] but Designe polilínea 2D o [Radio]:. A keyword it leaves out is spelled as in keywords.
  • A linetype's or hatch pattern's name is drawing data, the same in every language, and may be left out.

Messages are compiled at build time

Messages are ICU MessageFormat, the syntax of next-intl and FormatJS: {count, plural, one {…} other {…}}, {name}, and a <keywords> tag. The engine ships only a formatter of under 1 KB, with no parser, so a catalog holds compiled messages, and your build compiles them with icu-minify/compile:

import compile from "icu-minify/compile" // in your build, never in the page's bundleimport type { EngineLocale } from "@mellowcad/engine"
const fr: EngineLocale = {  tag: "fr",  interface: { byLayer: "DuCalque" }, // a message without arguments is already compiled  commands: {    names: { LINE: ["LIGNE", "L"] /* …every command */ },    messages: { unknownCommand: compile('Commande "{name}" inconnue.') },  },}
  • A message takes the arguments English's takes.
  • Lengths and angles arrive already formatted, with a point and latin digits, as AutoCAD writes them in every language. Counts are formatted by the engine: latin digits, no grouping.
  • In ICU, a < followed by a letter opens a tag: write '<'.

Names a host shows

What the engine names, a host reads from its state and never copies. state.names (EngineNames) holds, in the interface language:

FieldKeyed byWhere a host shows it
snapsSnapTypeSnap menus, the status bar
colorsSTANDARD_COLORS[].idColour pickers
byLayer—Colour, linetype and lineweight pickers
linetypesLINETYPES[].idLinetype pickers (a missing one is its id)
viewsViewPreset, and customThe view menu (state.view.preset)
viewCubeFacesViewCubeFaceThe ViewCube (state.viewCube)
compassCompassLetterThe ViewCube's compass

Tool labels and aliases are state.tools, command descriptions commandCatalog(), and property rows come worded in state.properties.info. A shortcut menu (state.shortcutMenu) brings the prompt's options worded in the command language (option.label) and the command its Repeat runs (repeat); its fixed entries come as ids (action) for you to word. Your own chrome — its buttons, its menus' other items — is yours to translate.

Versioning

The catalog's shape is public API: EngineLocale is typed from en.json, and your catalog is written against it. Renaming, moving or removing a key, or changing a message's arguments or tags, is a breaking change. Adding a key is not: your language falls back to English for it, or to the global name for a new command.