Documentation

CLI guide

Install Lingvo, create a project, keep translations in your repository, and make localization part of your build.

On this pageStart with a real project

Start with a real project

Create a Lingvo account, then run these commands in the repository containing your source strings. Replace my-workspace with your workspace slug from Settings and choose your target languages. init only writes local configuration; project create creates the hosted project.

Code example
lingvo login --device
lingvo init
lingvo project create --workspace my-workspace --name "My app" --locale de --locale ja
lingvo push --dry-run
lingvo push
lingvo status

Review lingvo.yaml before the first push. Creation requires an owner or admin, strings:write, and a credential without project restrictions. A workspace still has its plan’s project allowance. If the project already exists, use its slug in the config and skip creation.

Translate or review in the web editor before pulling. Your device login grants sync access, not provider configuration or machine-translation spend. Configure provider keys in workspace/project Settings. A CLI translation run also requires mt:spend and the appropriate role.

Code example
lingvo pull --dry-run
lingvo pull
lingvo qa --locale de --fail-on-error
git diff

Every command and option

Connect an MCP client

Install, update and pin a version

On macOS, Homebrew installs the CLI and Java 21 runtime. The shell installer needs Java 21 already available, checks the download, and installs inside your home directory. Run lingvo --version and lingvo --help to inspect your installation.

Code example
brew install merkost/lingvo/lingvo
# Or, with Java 21 installed:
curl -fsSL https://lingvo.dev/install.sh | sh
lingvo --version

For Homebrew updates, use brew update and brew upgrade. For the shell installer, rerun it. To pin CI, select an actually published release using LINGVO_VERSION before running the installer; do not assume a working-tree feature exists in an older release.

Code example
brew update
brew upgrade merkost/lingvo/lingvo
# Set LINGVO_VERSION to the release you have tested before installing in CI.

Check project create --help in your installed CLI before using it for setup. Use CLI 0.4.0 or later for the workflows below; if a command is unavailable in your installation, create the project in the web app or install a compatible release. Remove the Homebrew package with brew uninstall lingvo. For script installations, inspect the installer’s destination before removing its files; keep your repository config and translations.

Windows users can run the JVM distribution with Java 21. The shell installer and the shell examples here require a compatible shell, such as WSL; they are not PowerShell commands.

Authentication and scopes

Use device login interactively: it prints a code and a browser address. Approve only a pairing you initiated. The resulting credential is listed under Settings → API tokens and can be revoked there. No token needs to be copied into a command argument.

Code example
lingvo login --device
lingvo auth status
lingvo whoami
lingvo logout

For CI, create a token with the required scopes and store it as a CI secret. LINGVO_TOKEN overrides stored credentials. Stored credentials are kept per API host in ~/.lingvo/credentials with owner-only permissions. logout removes the stored credential; it cannot unset an exported environment variable.

Code example
printf '%s' "$LINGVO_API_TOKEN" | lingvo login --token-stdin
# Alternatively, read a named environment variable:
lingvo login --token-env LINGVO_API_TOKEN

API URL precedence is LINGVO_API_URL, then --api-url, then api_url in lingvo.yaml, then https://api.lingvo.dev. If the wrong server or identity appears, inspect auth status and your exported environment before logging in again.

A read-only CLI QA gate needs qa:run and strings:read. The SYNC set includes strings:read, strings:write, files:write, qa:run and glossary:read. Machine translation needs mt:spend; approval needs strings:approve. Provider keys, billing, members and token management require an interactive session, not an API token.

403 insufficient_scope names the missing scope. Changing project or workspace membership can also remove access immediately. A project-restricted token cannot create another project. Restrict the CI token after initial setup.

Configuration and platform paths

Use the project URL’s slug, such as my-app. Slugs contain lowercase letters, numbers and single hyphens, with at most 64 characters. The global --project option selects a project without rewriting lingvo.yaml. Invalid project values require correction before retrying.

Run init from the repository root. Commands find the nearest lingvo.yaml by walking up from the current directory, stopping at the Git repository boundary. Commit both config and lockfile, never credentials.

CLI 0.4.0 or later resolves configured resources, lockfiles, watch inputs and generated outputs beside the discovered config, even when invoked from a child directory. When using an older CLI, change into the directory containing lingvo.yaml before running repository commands. Explicit export --out paths still resolve from the directory where you invoke the command.

Code example
project: my-app
source_locale: en
files:
  - source: shared/src/commonMain/composeResources/values/strings.xml
    translation: shared/src/commonMain/composeResources/values-%locale%/strings.xml
    format: android_xml
  - source: iosApp/en.lproj/Localizable.strings
    translation: iosApp/%locale%.lproj/Localizable.strings
    format: ios_strings

%locale% expands to each target language. Android uses resource qualifiers, including region/script forms; prefer detected paths over guessing them. Apple String Catalogs and stringsdict have their own plural and conflict rules. A format that cannot express a plural is refused rather than silently flattened.

Code example
files:
  - source: locales/en.json
    translation: locales/%locale%.json
    format: json_flat
    plural_style: suffix

For flat JSON, nested is the default plural style. suffix groups dotted category keys such as cart.items.one and cart.items.other when other is present. Set it per file. Do not change the style of a production file without inspecting the resulting diff.

File formats, fidelity and limitations

generate may contain a single target or a list. Duplicate output paths and key-name collisions are errors. Generation fetches keys from the configured project, so push source keys first.

Code example
generate:
  - target: kotlin-object
    output: shared/src/commonMain/kotlin/app/Strings.kt
    package: app
  - target: swift-enum
    output: iosApp/Generated/L10n.swift

# Then run: lingvo generate

Sync, conflicts and deletion

push uploads sources; pull writes target files; sync performs both. Use --dry-run to inspect a sync plan before writing. watch is a long-running development loop, not a CI job.

Code example
lingvo push --dry-run
lingvo push
lingvo pull --locale de --dry-run
lingvo pull --locale de
lingvo sync --dry-run

Missing source keys are kept by default. To obsolete keys, use --obsolete-missing. When the server asks for a count, inspect the same dry run and pass its exact --confirm-obsolete count. Do not add that flag merely to silence an unexpected refusal.

Code example
lingvo push --dry-run --obsolete-missing
# Only after reviewing the reported count and intended removals:
lingvo push --obsolete-missing --confirm-obsolete 12

The lockfile records the last synced content. A local translation edit can cause a conflict; inspect the two versions before choosing which one to retain. sync --adopt-local uploads the local translation instead of overwriting it. Do not delete the lockfile to bypass a conflict.

pull --approved-only selects reviewed output. --fallback fills missing values from source; without it, ordinary untranslated values are omitted where the format supports that. Format-specific plural/catalog rules still apply. Inspect git diff before committing.

Import translated files in the browser

An owner or admin can open the project’s Data page, upload the translated file and choose Destination file. Select the existing source file by its full project path, including app or module folders, then set the translation’s language. The local upload name can be different. Import format describes the uploaded content and can be changed without renaming the project file. Read the destination and changes in the preview before applying them.

Use a project file path when you need to enter the exact path from your repository setup. A source import can create a file; a translation import needs an existing one. Refresh file list after a CLI push. If the selected file is unavailable, retry the read or choose another destination; the upload is retained and imports are never retried automatically.

Translation, proposals and QA

The CLI supports Claude, OpenAI, DeepL, Google, LibreTranslate and MyMemory. Availability depends on effective provider settings, credentials and the provider service. Check provider readiness in Settings rather than assuming a provider is reachable because its ID is known.

Code example
lingvo usage
lingvo translate --locale de --provider deepl --only-empty --dry-run
lingvo translate --locale de --provider deepl --only-empty
lingvo qa --locale de --fail-on error

Translation --dry-run still asks the provider to generate suggestions and can spend credits or BYOK provider funds; it prevents saving, not spending. A read-only estimate in the web pretranslation flow is the way to inspect the credit estimate without generating output.

BYOK does not consume Lingvo platform credits; your provider bills its own usage. Platform credits are charged per target language and source-length band, with provider weighting. Check the current pricing and estimate before running large batches. Rejected machine results are reported and are not saved.

Run qa --fail-on error for a release gate; --fail-on warning is stricter and --fail-on none reports without failing the command. --fail-on-error remains an alias. Project QA settings affect the result. Passing these checks validates the supported structural rules, not the linguistic accuracy of every translation.

Code example
lingvo explain locale ru
lingvo plurals required --locale ru
lingvo validate
lingvo verify
lingvo diff

Use propose when an agent should suggest text for a person to review. Proposals and translations are different records; an agent proposal is not approval. scan reports source references, explain describes a locale/format/check/error, and complete provides machine-friendly completions. The command reference lists their exact options.

JSON, exit codes and retry behavior

--json explicitly requests machine output. LINGVO_JSON also enables it, and output is automatically JSON when stdout is not a terminal. A regular successful command writes one result envelope to stdout; progress and diagnostics use stderr. watch is an event stream and mcp uses JSON-RPC.

Code example
{
  "schemaVersion": "1.0",
  "command": "project create",
  "ok": true,
  "data": {"project":"my-app","workspace":"my-workspace","nextCommand":"lingvo push"},
  "rows": [],
  "warnings": [],
  "stats": {}
}

The envelope above illustrates the common shape; each command supplies its own data, rows and stats. A command can return useful output with ok:false and a nonzero exit code. Parse JSON rather than human tables or wording.

Code example
0  Success
1  Content failure, conflict, rejected output, or failed QA
2  Invalid command, argument or configuration
3  Missing or rejected authentication/authorization
4  Network, rate-limit or server availability failure

An error that prevents a result is reported on stderr as an envelope containing schemaVersion, ok:false and error with code, message, retryable, optional remedy and HTTP status. Check the process exit code. Only availability failures are candidates for backoff; after an uncertain mutation, inspect server state before retrying creation or spending.

Try a source change

Start with a dedicated demo project containing an English welcome string and its German translation. No AI provider is needed. A translation can pass QA today and become invalid when its source changes tomorrow.

Code example
<string name="welcome">Welcome back</string>
<!-- German -->
<string name="welcome">Willkommen zurück</string>

After importing both languages, add a name placeholder to the English source. Push that source change, then run the hosted QA gate. The old German translation now lacks the placeholder, so the gate should exit 1 with a placeholder mismatch.

Code example
<string name="welcome">Welcome back, %1$s</string>
Code example
lingvo push --dry-run
lingvo push
lingvo qa --locale de --fail-on error

Update the German translation to preserve the placeholder, upload it and run QA again. Expect exit 0, then preview the pull before writing local files. A network or authentication error is not the expected failed QA result.

Code example
<string name="welcome">Willkommen zurück, %1$s</string>
Code example
lingvo push --translations --locale de
lingvo qa --locale de --fail-on error
lingvo pull --dry-run --locale de
lingvo pull --locale de

Use Android XML paths in lingvo.yaml for this example. Local validate checks source/configuration; hosted qa checks saved translations. Passing structural QA does not approve a translation or prove its linguistic quality.

Check checkout resources

Checkout checks require CLI 0.4.0 or later. Confirm that qa --help lists --local, --locales and --require-reviewed-current before adding these commands to CI. The reviewed/current policy also needs a compatible service advertising that optional check; the installed version alone does not establish service support.

Code example
lingvo qa --local --locale de --fail-on error
lingvo qa --local --locales de,fr --fail-on error
lingvo qa --local --locale de --require-reviewed-current --fail-on error

Basic local QA reads the actual configured source and target files in your checkout. Supply exactly one explicit --locale or --locales set; local mode refuses --tag. It needs no credentials or network and changes no files. It checks supported resource structure and default rules, including missing strings, placeholders and complete plural forms. It does not evaluate hosted glossary, exemptions or severity settings, linguistic accuracy, or native compilation and runtime.

Add --require-reviewed-current to compare those captured checkout resources with hosted approval, the exact current source basis, configured blocking QA and unresolved review or questions. This requires a credential with strings:read and qa:run, project access and a service advertising the optional check. A required policy failure exits 1 even with --fail-on none; unsupported service or invalid setup exits 2. There is no legacy fallback. The result covers the declared files and languages at the stated time and cannot authorize a future build or download.

If an unresolved question blocks the check, open the string in the editor’s Discuss panel. A workspace member with translation-write access can resolve the comment after addressing it, or reopen it when more work is needed. Resolving preserves the discussion and does not approve or refresh a translation. Run the check again after the review is complete.

In required reviewed mode, --fail-on uses the hosted project's configured severities and exemptions. Local default findings remain visible and do not override that policy. Complete capture and supported resource parsing remain mandatory.

The existing flat JSON envelope keeps command: qa and puts mode, gateLevel, gatePassed and the full localReport in data; reviewed mode also includes reviewedCurrentReport. Each report names its policy, complete scope, counts, findings and captured physical hashes. A report-only local command can exit 0 with ok: true while localReport.passed is false. Check the declared policy verdict as well as the process exit code.

For a refusal, inspect the exact key, language and file reported. Repair local structure in the checkout; review changed or unknown source basis in the web editor, then deliberately approve or reaffirm only after inspecting that source. Run the check again against the files you intend to ship. The separate optional hosted status on the project overview does not certify arbitrary local bytes.

CI recipes

Run the gate with a project-scoped token stored in CI secrets. Provision the project during setup, not during every build. This gate checks hosted translations; push reviewed source changes before relying on it to check a new release. Pin the CLI release that you have tested and provide Java 21 on the runner.

Code example
name: Localization QA
on: [push, workflow_dispatch]
permissions:
  contents: read
env:
  LINGVO_VERSION: ${{ vars.LINGVO_VERSION }}
  LINGVO_SHA256: ${{ vars.LINGVO_SHA256 }}
jobs:
  qa:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "21"
      - name: Install verified CLI archive
        shell: bash
        run: |
          set -euo pipefail
          [[ "$LINGVO_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
          [[ "$LINGVO_SHA256" =~ ^[a-fA-F0-9]{64}$ ]]
          archive="$RUNNER_TEMP/lingvo.zip"
          curl --fail --silent --show-error --location --proto '=https' \
            "https://github.com/Merkost/homebrew-lingvo/releases/download/v${LINGVO_VERSION}/lingvo-${LINGVO_VERSION}.zip" \
            --output "$archive"
          printf '%s  %s\n' "$LINGVO_SHA256" "$archive" | sha256sum --check --status
          unzip -q "$archive" -d "$RUNNER_TEMP/lingvo-cli"
          chmod +x "$RUNNER_TEMP/lingvo-cli/lingvo-${LINGVO_VERSION}/bin/lingvo"
      - name: Check hosted translations
        env:
          LINGVO_TOKEN: ${{ secrets.LINGVO_TOKEN }}
        run: '"$RUNNER_TEMP/lingvo-cli/lingvo-${LINGVO_VERSION}/bin/lingvo" qa --fail-on error'

Set LINGVO_VERSION and LINGVO_SHA256 repository variables to a reviewed published release and its ZIP digest. Store your QA token as the secret LINGVO_TOKEN. The checksum is pinned independently of each download; do not fetch a new digest on every run. This example uses readable action tags; pin third-party actions to reviewed commits if your repository policy requires it. Do not expose secrets to untrusted pull-request code.

For scheduled translation updates, run pull and QA, inspect the diff, then use your repository’s normal pull-request mechanism. Grant write permissions only to that job. Lingvo’s read-only GitHub App does not need broader repository permissions for a customer-owned CI workflow.

Approved translations, checked before the PR

Use the optional reviewed/current recipe when each update must contain approved text for the known current source. It pulls only approved translations, checks every configured resource across your selected languages, and limits the PR to verified target files, Apple plural companions, shared catalogues and the sync lockfile.

This recipe requires CLI 0.4.0 or later and a compatible service supporting the reviewed/current checkout policy. Confirm the required flags with qa --help. Unsupported commands, failed review checks, changed resource bytes or unexpected file changes stop the job. It never falls back to a weaker gate.

Download the complete reviewed translation PR workflow

Code example
Secret:
  LINGVO_REVIEWED_TOKEN        strings:read + qa:run; project-restricted
Repository variables:
  LINGVO_REVIEWED_CLI_VERSION  reviewed release, 0.4.0 or later
  LINGVO_REVIEWED_CLI_SHA256   independently reviewed ZIP digest
  LINGVO_REVIEW_LOCALES        de,fr-CA
  LINGVO_REVIEW_DIRECTORY      . (directory containing lingvo.yaml)

Save the download as .github/workflows/lingvo-reviewed-translations.yml. Choose explicit enabled target languages and a separate custom token with only strings:read and qa:run. Pin the CLI version and ZIP digest independently. Scheduled and manual runs always use the trusted default branch; reports stay outside the checkout.

Replace or disable the ordinary pull-translations job before adopting this recipe. Both use chore/lingvo-translation-update, so install only one updater. You can retain the source-push job. Changes update the same PR; unchanged pulls create no new update.

Enable GitHub Actions pull-request creation in repository settings. A PR created with the default GITHUB_TOKEN does not trigger other push or pull_request workflows. Arrange native app checks and human review before merging. Passing this check verifies the captured checkout at that moment; it does not approve a future release.

GitHub PR action setup and behavior

Troubleshooting and recovery

Email verification required: sign in to Lingvo as the account that owns the CLI credential or agent connection. Open the verification notice (under more notices if needed), request a link and confirm your address. Then retry the refused operation; do not rotate tokens to bypass verification. Lingvo-funded translation requires a verified account. BYOK, manual work, import, export, review and QA remain available.

Command not found: restart the shell or add the installer’s bin directory to PATH, then check Java and lingvo --version. A desktop MCP client may have a different PATH; use an absolute executable path.

Config not found: run init at the correct repository root or enter the directory containing the config. Do not copy a sibling project’s lockfile. Invalid locale: use explain locale with a BCP-47 tag and confirm the target is enabled in project Settings.

Project not found: check the configured slug, API host, membership and token allowlist. For a new project, create it explicitly. A 404 can intentionally hide a project the credential cannot access; it is not evidence that the project was deleted.

Insufficient scope: grant only the missing permission through a new token. Provider configuration requires an interactive session. Insufficient credits: use a configured BYOK provider, wait for reset or inspect the available paid plan; manual work, export and QA remain available.

Unexpected diff or conflict: stop before committing, retain your local changes, inspect the lockfile and compare with verify/diff. Use --adopt-local only when the local translation is the version you intend to upload. Report the CLI version, command, format and redacted error when requesting help; never include tokens or provider keys.

Complete command reference

MCP setup and troubleshooting