StringLane

Browse docsRun Localization Checks in CI
How-To

Run Localization Checks in CI

Gate a pull request on missing keys and broken placeholders with stringlane check. Exit codes, a machine-readable report, a baseline for an unclean repository, and the state-directory setting CI runners need.

Last updated

The whole gate is one command that already sets the right exit code, so there is no scripting to wrap around it.

GitHub Actions#

name: Localization
on: [pull_request]
 
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm install -g @stringlane/cli@0.3.2
      - run: stringlane check .

Pin the version. The CLI is at 0.x, which is the notation for a command surface that is not promised yet, and a pipeline should not change behaviour because the registry served a new minor that morning.

What the exit codes mean#

CodeMeaningWhat your pipeline should do
0No issuesContinue
1The project has localization issuesFail the job
2The command could not runFail the job, and look at the message
3Something stringlane depends on did not answerNot reachable from check

Keeping 2 separate is the point. A missing stringlane.yaml, an unreadable file or a bad flag is not the same event as a genuinely broken translation, and a pipeline that collapses them tells you the wrong thing on a bad morning. --fail-on cannot suppress a 2.

3 exists for the same reason one step further out: the command itself was fine and something it asked did not answer. Only stringlane upgrade can return it, so a check job will never see one.

The unused-key scan#

check also reads your source code and reports keys nothing calls, and keys your code calls that do not exist. Both land at info severity, so upgrading the CLI does not turn your pipeline red and a committed baseline does not flip.

Opt in when you want it to:

stringlane check . --fail-on unused_key

Or skip the scan entirely, which is worth doing on a runner that does not check out your application source:

stringlane check . --no-scan

Passing both is refused rather than accepted: with the scan off nothing can emit that code, and a gate that can never fire looks exactly like a clean project. What the scan can and cannot see: Unused keys.

Start green on a repository that is not#

A gate that fails on its first run gets removed. Record today's findings, once, and commit the result:

stringlane baseline write . --yes
git add .stringlane/baseline.yaml

From then on only new issues fail the build. Accepted ones are still counted and printed in every run, so the debt stays visible rather than disappearing. Full behaviour in Inspect and check.

Keep the report#

stringlane check . --json --output stringlane-report.json

Upload it as an artifact, or parse it to post a comment. The envelope is stable and versioned; its shape is in the protocol reference.

      - run: stringlane check . --json --output stringlane-report.json
        continue-on-error: true
      - uses: actions/upload-artifact@v4
        with:
          name: stringlane-report
          path: stringlane-report.json

continue-on-error on that step only, so the report is still uploaded when the check fails — which is the run you actually want it for. Keep a plain stringlane check . step afterwards if you want the job itself to go red.

Two things CI runners get wrong#

No writable home directory. StringLane keeps a small amount of its own state outside your repository, in the usual per-user location for the platform. On a runner or in a container where that has nowhere to go, point it somewhere writable:

export STRINGLANE_STATE_HOME="$RUNNER_TEMP/stringlane"

It must be an absolute path. A relative value is ignored rather than honoured, because otherwise the state would land wherever the command happened to be started from.

Telemetry. The CLI sends anonymous usage data by default, and sends nothing at all when it detects a continuous-integration environment. There is nothing to configure. If you want to be explicit anyway, DO_NOT_TRACK=1 or STRINGLANE_TELEMETRY=0 in the job environment says so.

What CI must not run#

Every writing command refuses when there is no terminal to ask at, unless you pass --yes:

refusing to write your localization files without confirmation.
stdin is not a terminal, so there is nobody to ask — pass --yes to confirm up front.

That refusal is the feature. Do not add --yes to apply or update in a pipeline to get past it: those commands change your translations, and a job that translates on its own is a job that commits work nobody read. Gate on check, and let a person or an agent do the writing.

Frequently asked questions

How do I fail a build when a translation is missing?
Run stringlane check in the job. It exits 1 when the project has localization issues and 0 when it is clean, so the step fails on its own with no scripting around it.
Can stringlane check modify files during a CI run?
No. check is read-only and writes nothing at all, and every command that does write refuses to run without a terminal unless it is passed --yes explicitly. A CI job cannot accidentally commit a translation decision nobody made.