Skip to content

Automatic build-time translation for datavis locales via Loco #340

Description

@horner

Goal

Keep the packages/datavis i18next locales translated automatically via Loco: push new English keys, AI-translate on the server, pull translated JSONs back into the repo. The build never depends on the Loco server at runtime — locale files stay committed (File-Mode philosophy).

flowchart LR
    Source[en-US.json<br/>source of truth] -->|1. push keys| Register[POST /api/textnodes<br/>+ approve]
    Register -->|2. find gaps| Gaps[GET /api/textnodes/untranslated-ids]
    Gaps -->|3. translate| AIJob[POST /api/ai-jobs<br/>poll until done]
    AIJob -->|4. pull| Export[GET /api/export/:lang]
    Export -->|5. write| Locales[src/i18n/locales/*.json<br/>committed to repo]
Loading

Milestone 1 — Connectivity & key-model spike

  • Confirm/create a Loco project for datavis and obtain LOCO_API_KEY
  • Curl smoke tests: GET /api (health), GET /api/languages, POST /api/textnodes with sample keys, GET /api/export/{lang}
  • Decide the key model (blocking): datavis uses ID keys (t('COLUMN_CONFIG') || 'Column configuration') while Loco is text-keyed — push English fallback text as the Loco key with context: 'datavis:<KEY>', or push the raw key?
  • Map repo locale names (zh-Hans-CN, es-MX, …) to Loco language codes (GET /api/languages)
  • Exit criteria: one key travels en-US.json → Loco → AI translation → exported JSON in the expected shape

Milestone 2 — loco-sync.mjs pull path (read-only half)

  • Create packages/datavis/scripts/loco-sync.mjs (zero-dep Node, same style as check-i18n-keys.mjs); config via LOCO_API_URL / LOCO_API_KEY env vars
  • --pull-only mode: GET /api/export/{lang} per language in src/i18n/config.ts, remap to repo keys, write src/i18n/locales/*.json (sorted keys, 2-space indent, trailing newline; skip unchanged files)
  • Keys missing on Loco keep their committed value — never delete translations on pull
  • Add "i18n:pull": "node scripts/loco-sync.mjs --pull-only" to packages/datavis/package.json
  • Exit criteria: pnpm i18n:pull yields a clean/correct diff; pnpm lint:i18n passes; Storybook renders fr-FR unchanged

Milestone 3 — Push + AI-translate path (full sync)

  • Push: diff en-US.json keys against Loco (GET /api/translations/all), register new ones (POST /api/textnodes), bulk-approve (PATCH /api/textnodes/status)
  • Translate: per language GET /api/textnodes/untranslated-idsPOST /api/ai-jobs → poll GET /api/ai-jobs/{jobId} with timeout, then pull
  • Summary output (keys pushed / translated / files changed); nonzero exit on job failure or timeout
  • Add "i18n:sync": "node scripts/loco-sync.mjs" to packages/datavis/package.json
  • Exit criteria: add a new key to en-US.json, run pnpm i18n:sync, all 9 locale files gain a translated entry

Milestone 4 — Build & CI integration

  • Local build hook: sync before build only when LOCO_API_KEY is set; skip silently otherwise so offline/contributor builds work from committed locales
  • PR check workflow (thin wrapper around the script): i18n:pull + git diff --exit-code fail-on-drift
  • Scheduled/manual workflow: full i18n:sync that opens a PR with refreshed locale files (never auto-commits to main)
  • Add LOCO_API_KEY as a repo secret
  • Exit criteria: CI goes red when a new key lacks translations; scheduled run produces a locale-update PR

Milestone 5 — Hardening & docs

  • Retry/backoff on API calls
  • Placeholder guardrail: verify i18next interpolations ({{count}} etc.) survive AI translation; fail the key otherwise
  • Optional: seed Loco glossary with domain terms (chart, pivot, perspective, …)
  • README section in packages/datavis (workflow diagram, env vars, commands); note in TESTING.md if the drift check lands in CI
  • Exit criteria: a developer new to Loco can add a key and get translations by reading the README

Risks

Risk Mitigation
Loco is text-keyed, datavis is ID-keyed Milestone 1 spike decides mapping before any code
AI mangles i18next placeholders Milestone 5 validator; Loco's slot system helps
CI spend on AI jobs Full sync only on schedule/manual; PRs are pull-only
Language code mismatches Explicit mapping table validated against GET /api/languages

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions