Capabilities

Each part of the plugin API - config hooks, formats, locale finders, scanners, extractors, checks, reporters, placeholders and more.

A capability is one kind of contribution to x18n. A plugin can add any number of them. This page describes each one. The types are at the end of the page.

Config hooks

  • config(draft, ctx) changes a draft of the config before it is final. ctx.user is the config as the user wrote it, and ctx.host reads files. Presets set their defaults here. The config of the project is applied after every hook, so it wins.
  • configResolved(config) receives the final, frozen config. Read plugin options from config.pluginConfigs here.

x18n records which plugin changed which value. x18n doctor shows it as plugin:<name>.

Manifest readers

manifests reads the dependency names from a manifest file, for preset detection. The built-in readers cover package.json, composer.json, pubspec.yaml and Gemfile. A reader has an id, a file name and a read(text) function.

Locale finders

localeFinders find resources. find(ctx) returns candidates: a path, a locale, a namespace, and optionally a format id, a block inside the file, and a read-only flag. resolveNew(target, ctx) returns the path of a new file for a locale and namespace, for edits that create files.

The built-in finders are pattern (the resources patterns), sfc-blocks (Vue <i18n> blocks), sfc-fluent-blocks (fluent-vue <fluent> blocks) and nls (VS Code package.nls.json).

Formats

formats read and write one file format. A format has an id and the extensions it reads.

  • parse(text, ctx) returns the message tree, and optionally the text range of each key and value. The ranges power go to definition and diagnostics.
  • edit(text, ops, ctx) returns text edits for a list of set and delete operations. x18n prefers it, because it keeps comments and layout.
  • serialize(tree, ctx) writes a whole file. x18n uses it to create files, and when edit is missing.

The built-in formats are:

IdExtensionsFiles
jsonjsonJSON
jsoncjsoncJSON with comments
json5json5JSON5
yamlyml, yamlYAML
popo, potgettext catalogs. Each msgid is a key.
propertiespropertiesJava properties
ftlftlFluent. A message attribute is the key message.attribute.
iniiniINI. A section is the first part of its keys.
phpphpPHP files that return an array literal
jsjs, mjs, cjs, ts, mts, ctsJS and TS modules whose default export is an object literal

Every built-in format has edit. To set a value, the js and php formats replace only the literal of that value. To add or delete a key, they write the object literal or the array again. A format that only reads makes its resources read-only.

Scanners

scanners find usages in source files. A scanner has an id, the file extensions it reads (* for every file), and scan(file, ctx). ctx.calls holds the call specs of the project. The result holds:

  • usages: the raw key text, its range, and whether the key is static, a prefix, or unknown,
  • scopes: regions with a default namespace or key prefix,
  • keySlots: positions where key completion makes sense,
  • hardStrings: text that is probably meant to be a message.

The built-in scanners are js (JS, TS, JSX), vue, svelte and regex. Most libraries need no new scanner: a call spec in usage.calls or a regex scanner is enough.

Extractors

extractors add extraction templates for a library: the code that replaces a hard string, per kind of hard string and per file glob.

Checks

checks produce diagnostics. A check has an id, a defaultSeverity and a run(ctx) generator. ctx gives the keys, messages, usages, coverage, unused keys, placeholders, hard strings and file texts. It also gives ctx.options, the options from [severity, options] in the config. See the example.

A diagnostic can carry fixes. A fix has a title and a list of edit operations. The editor shows fixes as quick fixes.

Reporters

reporters turn the diagnostics and the coverage of each project into text, for x18n check --reporter <id>. report(inputs, ctx) returns a string. ctx.displayPath turns a project path into a path relative to the current directory, and ctx.color tells whether colors are allowed.

Placeholder syntaxes

placeholders describe how placeholders look in messages. find(value) returns the placeholders of a value. validate(value) returns syntax errors, for the invalid-syntax check. example is the text that prompts show. reference: true means that the placeholder names another key, which then counts as used.

Keys

  • normalizeKey(raw, ctx) turns the raw key text of a usage into the canonical key. The first plugin that returns a value wins.
  • derivedKeys are rules such as {key}_*. When a key is used, the derived keys count as used.

Resource transforms

transformResource changes how the tree of a file maps to keys. Use it for files that wrap the messages, such as a file whose messages sit under a top-level messages key.

  • read(tree, ctx) returns the tree of keys after parsing.
  • toStored(path, ctx) returns the path in the file of a key path. x18n writes edits to that path.
  • applies(ctx) tells if the transform applies to a resource. Without it, the transform applies to every resource.

The ruby-rails preset uses a transform for the top-level locale key, and the chrome-extension preset for the message field.

Translate prompts

translatePrompt(draft, ctx) adds rules and notes to the translate prompt. ctx holds the source and target locales and the locale information of the target, such as its plural categories. The i18next preset uses it to explain plural suffixes.

Locale information

localeInfo(code) returns the display name, the flag or other data of a locale code. The first plugin that returns a value wins.

Order and runtime

  • enforce: 'pre' runs a plugin before the others, and enforce: 'post' runs it after them.
  • runtime: 'node' marks a plugin that needs Node. Runtimes without Node skip it.
  • A capability with the id of an earlier one stops the load with X18N1002. Set override: true on the later one to replace the earlier one.

Types

export interface Plugin {
  /** Unique, kebab-case. Built-ins use the `x18n:` prefix. */
  readonly name: string
  readonly enforce?: 'pre' | 'post' | undefined
  /** Where the plugin can run. Default 'any'. */
  readonly runtime?: 'any' | 'node' | undefined

  /** Change the config before it is final. Presets use this to set defaults. */
  readonly config?: ((config: ConfigDraft, ctx: ConfigContext) => void | Promise<void>) | undefined
  readonly configResolved?: ((config: ResolvedConfig) => void) | undefined

  readonly manifests?: readonly ManifestReader[] | undefined
  readonly localeFinders?: readonly LocaleFinder[] | undefined
  readonly formats?: readonly Format[] | undefined
  readonly scanners?: readonly Scanner[] | undefined
  readonly extractors?: readonly Extractor[] | undefined
  readonly checks?: readonly Check[] | undefined
  readonly reporters?: readonly Reporter[] | undefined
  readonly placeholders?: readonly PlaceholderSyntax[] | undefined
  readonly histories?: readonly HistoryProvider[] | undefined

  /** Turns raw key text into canonical form. The first plugin that returns a value wins. */
  readonly normalizeKey?: ((raw: string, ctx: KeyContext) => string | undefined) | undefined
  readonly derivedKeys?: readonly string[] | undefined
  readonly transformResource?: ResourceTransform | undefined
  readonly translatePrompt?: ((draft: PromptDraft, ctx: PromptContext) => void) | undefined
  /** Display data for a locale code. The first plugin that returns a value wins. */
  readonly localeInfo?: ((code: LocaleCode) => Partial<LocaleInfo> | undefined) | undefined
}

export interface Preset {
  readonly name: string
  /** Decides if the preset applies. Without `detect`, the preset applies only when listed in config. */
  readonly detect?: ((ctx: DetectContext) => boolean | Promise<boolean>) | undefined
  /** When true and detected, presets that come after it are not applied. */
  readonly exclusive?: boolean | undefined
  readonly plugins: readonly (Plugin | Preset)[]
}