ResilientLang docsDeveloper documentation (draft, product name is a placeholder)

CLI and SDKs

Status. The CLI and both clients exist in the repository (tools/cli, packages/sdk-js, src/ResilientLang.Client). None is published to npm or NuGet, and there is no default API address because the domain is undecided: you always pass --api-url or RL_API_URL.

The rl command line#

Zero dependencies, Node 18.17 or newer. From the repository: node tools/cli/bin/rl.mjs help.

printf %s "$RL_SECRET_KEY" | rl login --api-url https://api.example --key-stdin
rl sites create --name Shop --source en --targets fr,de
rl push strings.json --site <id>            # --dry-run works offline
rl translate --wait && rl publish --wait
rl export --format xliff --out backup.xlf
CommandWhat it does
login, logout, whoamiStore, remove and show the credentials (key from stdin or RL_API_KEY)
doctorRead-only check of the key, API URL, site, scopes, allowance and the signature of the published manifest: --offline (no network), --json. Exit 0 unless a check fails (3 auth, 5 plan, 6 unavailable, otherwise 1)
siteslist, get, create, update
push FILESend source strings: a JSON array, {"items":[...]} or one string per line; - reads stdin; --dry-run
pullBundle mode (default): download and verify the published bundle
pull --translationsAPI mode: write <lang>.json with {key: text} from the API
translateMachine-translate missing strings: --lang, --force, --wait
scanCrawl pages: --url (repeatable) and --sitemap, --wait
publishBuild and publish: --always, --wait
jobsget ID, wait ID [--timeout seconds]
exportDownload everything you own: --format json|xliff|xliff2|po|pot|csv|tmx, --lang, --out
import FILEUpload translations: the export file, a [{key, lang, text, status?}] array, or xliff, xliff2, po, csv, tmx, android, messages-json or keyvalue-json (auto-detected). Flags: --format, --lang, --reference FILE, --status machine|reviewed|manual, --dry-run
quotaPlan allowance and where you stand this period (GET /v1/usage/quota)
usageStrings, characters and engine cost: --from, --to
glossaryget, put FILE.json
keyslist, create --name N --scopes a,b, rotate ID [--grace s], revoke ID

Global options: --site ID (else RL_SITE, else .resilientlang.json), --api-url URL, --json, --help. Environment: RL_API_KEY, RL_API_URL, RL_SITE, RL_CONFIG_DIR, RL_BASE, RL_PUBLIC_KEY, RL_SITE_KEY. Exit codes: 0 ok, 1 error, 2 usage, 3 not authenticated or not allowed (401 or 403), 4 a job you waited for failed or timed out, 5 a plan, allowance or licence limit (402), 6 service unavailable or rate limited (429, 502, 503, 504 or a network error): try again later.

rl pull in bundle mode#

Bundle mode needs no API key and never sends one. It fetches the static files from the delivery origins and verifies them with the loader's rules: a mandatory Ed25519 signature over the canonical manifest, the kid, the SHA-256 of each file, and rollback protection. The result is a folder (./bundle by default) holding manifest.json and <version>/<lang>.<hash8>.json, which is a complete offline copy you can host yourself.

rl pull --base https://cdn.example/ --public-key <ed25519-base64url> --site-key rl_site_live_... --lang fr,de --out bundle

There is no default origin; pass --base (or RL_BASE).

Key handling#

The key is read from stdin or RL_API_KEY (passing --key prints a warning). Credentials are stored in a file readable only by you under $RL_CONFIG_DIR, $XDG_CONFIG_HOME/resilientlang or ~/.config/resilientlang. All output is redacted of rl_sec_ values. A .resilientlang.json project file is refused if it contains a key, so it is safe to commit.

JavaScript SDK#

@resilientlang/sdk, zero dependencies, Node 18.17 or newer.

import { ResilientLangClient } from '@resilientlang/sdk';
const rl = new ResilientLangClient({ baseUrl: process.env.RL_API_URL, apiKey: process.env.RL_API_KEY });
const site = await rl.sites.create({ name: 'Shop', sourceLanguage: 'en', targetLanguages: ['fr'] });
await rl.strings.submitAll(site.id, [{ text: 'Add to cart' }]);
const { jobs } = await rl.translate(site.id);
await rl.jobs.wait(jobs[0].id);
await rl.publish(site.id);

The key is sent only to baseUrl (https, or http on localhost), redirects are never followed, requests time out, 429 is retried honouring Retry-After, and other retries happen only where repeating is safe. @resilientlang/sdk/testing provides an in-memory mock server for offline tests. The SDK's own tests fail when openapi.json gains an operation without a client method.

Python, PHP and Go#

Clients for these three languages exist too (WP-54, not yet merged or published): see Python, PHP and Go SDKs.

.NET client#

ResilientLang.Client: services.AddResilientLangClient(configuration) binds the ResilientLang:Client section (BaseUrl, ApiKey from user secrets or environment, Timeout, MaxRetries), validates at start and disables redirects.

Both clients cover the control-plane operations in the API reference. They do not yet cover webhooks, audit, billing, licensing or team routes, which are not in the OpenAPI file.