Skip to content

SDK: types CLI

@lionrapid/cli turns your translations into TypeScript types, so translation keys and their ICU parameters become compile-time errors instead of runtime surprises: t('welcome', { name }) compiles, t('welcome') with a missing parameter does not, and a typo in the key itself is caught by the compiler.

Install it as a dev dependency:

Terminal window
npm install --save-dev @lionrapid/cli

The binary is lionrapid. Type generation lives under the types command, with two subcommands: generate and check.

The CLI reads lionrapid.yml from your project root — it is the single source of truth for which files hold your translations, so the type commands take almost no flags. Scaffold one with lionrapid init, or write it by hand:

server: https://your-lionrapid-host
sourceLocale: en
targetLocales: [es, de]
namespace: app
files:
- format: json
source: locales/en.json
target: "locales/{locale}.json"

target carries a {locale} placeholder, which the CLI expands per target locale. source may be a glob (locales/en/*.json), in which case target and namespace must carry a {basename} token.

Supported format values: json, yaml, po, ios-strings, xcstrings, android-xml, arb, xliff.

Two optional keys control type output directly:

Key Default Meaning
typesOutput types/translations.d.ts Where the declaration file is written
generateTypes false Regenerate types automatically after pull

Pass -c, --config <path> to any command to point at a config elsewhere.

By default the generator works offline, deriving keys and ICU parameter types from the local source files listed in your config:

Terminal window
npx lionrapid types generate
Option Alias Default Meaning
--output <path> -o types/translations.d.ts Path of the generated declaration file
--server off Read the schema from the backend instead of local files
--strict-icu off Strict ICU mode (see below)
--config <path> -c lionrapid.yml Path to the config file
--json off Emit the result as JSON on stdout

--server fetches the source-locale bundle from the platform instead of reading local files, which is the option you want when translators are adding keys that haven’t been pulled yet:

Terminal window
npx lionrapid types generate --server

This is the only type command that talks to the network, so it is the only one that needs an API key. Provide it with lionrapid login, which saves it to ~/.lionrapid/credentials, or set LIONRAPID_API_KEY for a one-off or CI run. The offline default needs no credential at all.

The file augments the core package’s TranslationKeys interface via module augmentation. Keys are sorted, keys without parameters map to Record<string, never>, and keys with parameters map to a typed object:

declare module '@lionrapid/core' {
interface TranslationKeys {
'app.title': Record<string, never>;
'items.count': { count: number };
welcome: { name: string };
}
}
export type AvailableKeys = keyof import('@lionrapid/core').TranslationKeys;

The core’s t() is typed conditionally against this interface, which is what produces the enforcement:

lionRapid.t('app.title'); // ok — zero-parameter key
lionRapid.t('welcome', { name: 'Ada' }); // ok
lionRapid.t('welcome'); // error: 'name' missing
lionRapid.t('items.count', { count: 'x' }); // error: count must be number
lionRapid.t('app.titel'); // unknown key: falls back to the
// untyped signature — no autocomplete, params unchecked

Import the generated file once (an entry-point import './types/translations.d.ts' or a tsconfig.json include that covers it) and restart the TypeScript server in your IDE after the first generation.

The CLI analyzes each key’s ICU message and its parameter names:

Signal in the message / name Inferred type
{count, plural, …}, selectordinal, number number
{gender, select, male {…} female {…}} 'male' | 'female' | (string & {})
Name is count / ends in Count number
Name starts is/has/can, ends enabled/visible/active boolean
Name is id / ends in Id/_id string | number
Name contains date/time/timestamp Date | string
Anything else string

By default the CLI runs in hybrid mode; --strict-icu switches to strict. The difference shows up in two places:

  • Select unions. Hybrid types a select parameter as the known options plus any string — 'male' | 'female' | (string & {}) — so autocomplete suggests the real options but unexpected values still compile (they hit the message’s other arm at runtime). Strict emits the literal options only, so an unknown select value is a compile error.
  • Missing other arms. ICU requires an other arm in every plural and select. Hybrid tolerates a message that lacks one; strict fails generation with an error, which is what you want in CI.

Start hybrid while translations are in flux; turn on --strict-icu once the catalog stabilizes.

check reports ICU problems in your source translations without writing anything:

Terminal window
npx lionrapid types check

It prints the key count and any issues found, and exits 2 when there are issues — a cheap gate for catching a malformed message before it reaches translators. Like the default generate, it reads local files only and needs no credential. Add --json to consume the result in a script.

pull downloads translations into your target files. Add --types to regenerate the declaration file in the same step, so freshly-synced keys are typed immediately:

Terminal window
npx lionrapid pull --types

Setting generateTypes: true in lionrapid.yml makes that the default and lets you drop the flag.

Regenerate types in CI and let tsc fail the build when code and translations have drifted apart:

- run: npx lionrapid types generate --strict-icu
- run: npx tsc --noEmit

That catches, at merge time: code using keys that no longer exist, parameters that changed type, and ICU messages missing their other arm. Add --server if you want CI checked against the live schema rather than the committed source files — that variant needs LIONRAPID_API_KEY in the job environment.

The CLI uses a stable exit-code taxonomy, so scripts can branch on the failure class:

Code Meaning
0 Success
1 Unexpected failure (file I/O, parse error, bug)
2 Bad config, or validation gaps such as ICU issues
3 Missing or invalid credentials
4 Request never reached the server, or a 5xx
5 Partial — some items succeeded and some failed

Type generation is progressive enhancement. Without the declaration file, every key is accepted with optional untyped parameters — the library behaves identically at runtime. Unknown keys keep working through the untyped fallback signature even after types are in place.