@lexeri/check-cli
v1.1.2
Published
Check documents for forbidden terminology with Lexeri — CI-friendly CLI
Maintainers
Readme
lexeri-check
CI-friendly CLI that checks documents for forbidden terminology with Lexeri.
It uploads the given file(s), runs a term check against the termbase linked to your API token, waits for the results, prints every matched term with its state, and exits with a CI-meaningful exit code.
Requires Node.js >= 20. No runtime dependencies.
Usage
lexeri-check [options] <file...>export LEXERI_API_TOKEN=eyJhbGciOi... # created in the Lexeri web UI
lexeri-check --locale en src/i18n/en.json Resources/Strings.resxExample output:
src/i18n/en.json (check a1b2c3, 214 sentences, 12.3s)
STATE TERM PREFERRED OCCURRENCES
not_recommended "whitelist" "allowlist" 4
preferred "email" - 2
6 matches: 4 not_recommended, 2 preferred → FAIL
1 file checked: 0 passed, 1 failed — FAIL (failing states: not_recommended, outdated)API token
Create an API token in the Lexeri web UI for the termbase you want to check against (see API tokens in the Lexeri help center). The token needs the check_text, read and show scopes. The termbase is derived from the token — no termbase flag is needed.
Pass it via the LEXERI_API_TOKEN environment variable (recommended for CI; use a masked variable) or --token.
Options
| Flag | Env var | Default | Description |
|---|---|---|---|
| -t, --token <jwt> | LEXERI_API_TOKEN | — | Lexeri API token (required) |
| -l, --locale <code> | LEXERI_LOCALE | detected at upload | Source language of the documents |
| --target-locales <csv> | — | — | Target locales for XLIFF (.xlf/.xliff) translation checks |
| --topics <csv> | — | — | Restrict matching to these topic identifiers |
| --tags <csv> | — | — | Restrict matching to terms carrying these tags |
| --filter <key>=<value> | — | — | Filter by a custom termbase field (repeatable, see below) |
| --fail-on <csv> | — | termbase's negative states | Term states that fail the check (see below) |
| --terms-url <url> | LEXERI_TERMS_URL | https://terms.lexeri.com | Terms service base URL |
| --files-url <url> | LEXERI_FILES_URL | https://files.lexeri.com | Files service base URL |
| --timeout <seconds> | — | 300 | Max wait per file for check completion |
| --keep-check | — | off | Keep the check and the uploaded document on the server after fetching results (deleted by default) |
| --json | — | off | Machine-readable JSON report on stdout |
| -v, --verbose | — | off | Log API requests to stderr (token never logged) |
Exit codes
| Code | Meaning | |---|---| | 0 | All files passed | | 1 | At least one match in a failing term state | | 2 | Usage or configuration error (bad flags, missing token/locale, file not found) | | 3 | Operational error (auth, network, API failure, timeout) |
With multiple files, the worst outcome wins: an operational error (3) beats failing terms (1) beats pass (0). A failure on one file does not stop the remaining files from being checked.
Failing states (--fail-on)
By default the CLI fetches your termbase and fails on its negative term states: not_recommended, outdated, plus any custom states flagged as replaceable. preferred and admitted matches are reported but never fail.
--fail-on replaces that set entirely, e.g.:
lexeri-check --fail-on not_recommended,deprecated,misspelled docs/manual.docxmisspelled is a pseudo-state: include it to fail on misspelled term matches.
Matches in states that are neither failing nor known-positive are shown as unknown and never fail the check — add them to --fail-on explicitly if they should.
Scoping the check (--topics, --tags)
By default a check matches against the whole termbase. To scope it — for example to check UI strings only against your UI terminology — restrict matching by topic and/or tag at check creation:
lexeri-check --topics 3fa5b2c1 --tags ui,frontend src/i18n/en.jsonTopics are referenced by their identifier; unknown topic identifiers are ignored by the server. These are the same filters the topic/tag pickers in the Lexeri check form use.
Custom field filters (--filter)
Termbases can define custom fields on term entries and terms (picklist, number, boolean, date). Filter by them with repeatable --filter <key>=<value> arguments, where the key is the backend parameter name: term_entry_field_<field-identifier> or term_field_<field-identifier> (date fields take _start/_end suffixes):
lexeri-check \
--filter term_field_a1b2c3=released \
--filter term_entry_field_d4e5f6=ui \
--filter term_entry_field_d4e5f6=web \
src/i18n/en.jsonRepeating the same key sends the values as an array, which is how multiple-selection picklist fields are queried. Values are validated server-side against the field's definition (e.g. picklist options); invalid queries are silently dropped by the server, not errored.
File formats
Files are parsed server-side, so the CLI does not filter by extension — the server decides what it can parse.
The formats most relevant for UI and software development:
- String resources:
.resx,.json,.properties,.strings,.po,.yaml/.yml,.ini,.csv,.xml,.twine - Translation interchange:
.xlf/.xliff,.sdlxliff,.mqxliff,.tmx— XLIFF files additionally support translation checks against target languages via--target-locales - Docs in the repo:
.md,.html,.txt,.srt,.dita/.ditamap
Office and DTP documents are supported as well: Word, Excel and PowerPoint including legacy and macro-enabled variants (.docx/.doc/.docm, .xlsx/.xls/.xlsm, .pptx/.ppt/.pptm), OpenDocument (.odt, .ods, .odp), Apple iWork (.pages, .key), plus .pdf, .rtf, .epub, .vsdx, .idml and .mif.
For string formats, matches are reported with the string keys they occur under, so violations can be traced back to the exact resource entry.
GitLab CI
Set LEXERI_API_TOKEN as a masked CI/CD variable, then:
terminology-check:
image: node:22-alpine
script:
- npx --yes @lexeri/check-cli --locale en src/i18n/en.json
rules:
- changes:
- "src/i18n/**/*"Checking all string resources of a given type, e.g. every .resx file (glob expansion is done by the shell — on minimal images, use find):
script:
- find . -name '*.resx' -exec npx --yes @lexeri/check-cli --locale en {} +Checking translated XLIFF files against a target language:
script:
- npx --yes @lexeri/check-cli --locale en --target-locales de translations/de.xlfTo keep a machine-readable report as an artifact:
terminology-check:
image: node:22-alpine
script:
- npx --yes @lexeri/check-cli --json --locale en src/i18n/en.json > term-report.json
artifacts:
when: always
paths: [term-report.json]With --json, the report goes to stdout and everything else (progress, warnings) to stderr, so redirection stays clean.
JSON report shape
{
"version": 1,
"verdict": "fail",
"exitCode": 1,
"failOn": ["not_recommended", "outdated"],
"termbase": { "identifier": "…", "name": "Marketing" },
"files": [
{
"file": "docs/manual.docx",
"status": "fail",
"checkIdentifier": "…",
"localeCode": "en",
"sentenceCount": 214,
"durationMs": 12300,
"totalMatches": 6,
"countsByState": { "not_recommended": 4, "preferred": 2 },
"misspelledCount": 0,
"matches": [
{ "term": "whitelist", "state": "not_recommended", "classification": "fail", "preferred": "allowlist", "occurrences": 4, "misspelled": false }
],
"error": null
}
]
}How it works
GET /termbases/current— resolves the termbase from the API token and derives the failing state set.POST /v2/documents(files service) — multipart upload, marked temporary; returns a short-lived document token.POST /checks— creates the check for the document, locale and termbase.GET /checks/:id— polled every 2.5s untilprogressreaches 100 (or--timeout).GET /term_checks/:id/matches— fetches the term matches, classifies each by its term state, prints the report.DELETE /checks/:id— removes the check and its uploaded document from the server (skipped with--keep-check; a failure here only warns and never changes the verdict).
