JSON contracts

The versioned JSON output of the CLI, shared by the LSP, the UI and MCP.

x18n has one versioned JSON contract. The CLI prints it with --json. The LSP x18n/* requests, the UI RPC functions and the MCP tools use the same shapes.

The current contract version is 2. A breaking change to any shape increases it. A new optional field does not.

Envelope

With --json, every command prints one JSON document to stdout:

export interface Envelope<T> {
  readonly x18n: number
  readonly command: string
  readonly data: T
  readonly diagnostics: readonly WireDiagnostic[]
  readonly errors: readonly ErrorInfo[]
}
  • x18n is the contract version.
  • command is the command name, for example keys or prompt translate.
  • data is the result of the command. It is null when the command failed.
  • errors holds the errors. In JSON mode, errors appear only here, so stdout is always valid JSON.
/** JSON form of an error: the `toJSON()` of a nostics diagnostic, without `stack`, `cause` and `data`. */
export interface ErrorInfo {
  /** The code, such as `X18N2002`, or the JS error name of an error without a code. */
  readonly name: string
  readonly why: string
  readonly fix?: string | undefined
  readonly docs?: string | undefined
}

docs is the URL of the error page of the code.

Diagnostics

Checks report diagnostics. x18n check --json puts them in diagnostics of the envelope, and its data holds a summary and the coverage per project. x18n get --json puts the diagnostics of one key in data. Both use this shape:

export interface WireDiagnostic {
  readonly project: string
  readonly code: string
  readonly severity: Severity
  readonly message: string
  readonly key: KeyString | undefined
  readonly locale: LocaleCode | undefined
  /** Path relative to the project root. */
  readonly file: string | undefined
  readonly range: WireRange | undefined
  readonly fixes: readonly Fix[]
}

export interface WireRange {
  /** 1-based. */
  readonly line: number
  /** 1-based, UTF-16 code units. */
  readonly column: number
  readonly endLine: number
  readonly endColumn: number
  /** 0-based UTF-16 offsets. */
  readonly start: number
  readonly end: number
}

export type Severity = 'error' | 'warning' | 'info' | 'hint'

export interface Fix {
  readonly title: string
  readonly ops: readonly EditOp[]
}

export type EditOp
  = | { readonly type: 'set', readonly key: KeyString, readonly locale: LocaleCode, readonly value: MessageLeaf }
    | { readonly type: 'rename', readonly from: KeyString, readonly to: KeyString, readonly usages?: boolean }
    | { readonly type: 'rename-prefix', readonly from: KeyString, readonly to: KeyString, readonly usages?: boolean }
    | { readonly type: 'delete', readonly key: KeyString, readonly locales?: 'all' | readonly LocaleCode[] }
    | { readonly type: 'extract', readonly items: readonly ExtractItem[] }
    /** Re-write resources with the configured sort and indent. */
    | { readonly type: 'format', readonly paths?: readonly string[] }

code is the check id, for example missing-key. See Checks.

Input of x18n apply

x18n apply and the MCP tool x18n_set_messages read messages per locale:

{
  "de": {
    "app.title": "Meine App",
    "nav.home": "Start"
  }
}

Extraction plan

x18n extract --plan <file> writes this file, and x18n extract --apply <file> reads it back after you edit it:

export interface ExtractPlanFile {
  readonly x18n: 1
  readonly project: string
  readonly items: readonly ExtractCandidate[]
}

export interface ExtractCandidate {
  readonly path: string
  /** 1-based line, for display. */
  readonly line: number
  readonly kind: string
  readonly text: string
  /** The text in the file at `range`, used to detect stale plans. */
  readonly original: string
  readonly range: TextRange
  readonly attribute: string | undefined
  readonly key: KeyString
  /** True when an existing key with the same source value is reused. */
  readonly reused: boolean
  /** The message to store: template arguments become placeholders. */
  readonly value: string
  readonly replacement: string
  readonly imports: readonly string[]
  /** Other templates that also apply, for surfaces that let users choose. */
  readonly alternatives: readonly string[]
}

Exit codes

Exit codeMeaningError codes
0Success. For x18n check: no errors, and no more warnings than --max-warnings.
1x18n check found errors or too many warnings, x18n apply rejected values, or the command failed with one of these errors.X18N2001, X18N2002, X18N2003, X18N2004, X18N3001, X18N4001, X18N4002, X18N4005, X18N4006, X18N4007, X18N4008
2Usage, config or plugin error. Errors without an x18n code also exit with 2.X18N0001, X18N0002, X18N0003, X18N1001, X18N1002, X18N1003, X18N1004
3A decision is needed and no answer was given. Run again with --yes.X18N4003
4Files changed while the command ran. Run it again.X18N4004