Guide · Migration
Migrate from i18next
Already shipping with i18next? @sonenta/react-i18next is a drop-in replacement for react-i18next — same useTranslation() and t() API, you just swap the provider and your translations come live from the CDN. Bring your existing locales/ JSON into Sonenta with one command, then keep your code exactly as it is. The import creates the missing keys, upserts every translation, and is fully idempotent, so you can re-run it from CI without fear. This is the whole path: install, import, publish, verify, wire up the SDK.
Before you begin
Three things, then you're ready to import:
- A project. Create one in the dashboard and copy its
project_uuid. - An API key with
mcp:*scope. The CLI runs against the MCP surface — a project-scoped key returns403. The CLI reference covers how to mint one. - Node 18+ and your existing i18next files, laid out as
<lang>/<namespace>.json(odd layouts are handled too).
1. Install and initialise
Install the CLI globally, scaffold a config that points at your project, and export your key.
terminal 1# one global install — gives you the `sonenta` command (Node >= 18)2npm i -g @sonenta/cli 4# scaffold sonenta.config.json and point it at your project5sonenta init --project <project_uuid> 7# the CLI talks to the MCP surface — use an mcp:* scoped key8export SONENTA_TOKEN=vrb_live_<prefix>.<secret> sonenta init writes a sonenta.config.json you can commit. In CI, skip init and pass --project plus the SONENTA_TOKEN environment variable. sonenta and SONENTA_* are canonical; the legacy verbumia command and VERBUMIA_* vars still work during the transition.
2. Preview, then import
The importer reads each file's path to infer its language and namespace, so a conventional locales/ tree needs no flags. Dry-run first to see the plan, then drop --dry-run to apply it.
your repo 1# the importer infers (language, namespace) from each path:2# <lang>/<namespace>.json3locales/4├─ en/5│ ├─ common.json → language en · namespace common6│ └─ checkout.json → language en · namespace checkout7└─ fr/8 ├─ common.json → language fr · namespace common9 └─ checkout.json → language fr · namespace checkout terminal 1# preview first — no writes, prints exactly what WOULD change2sonenta import "./locales/**/*.json" --dry-run 4# the real run — idempotent, safe to repeat5sonenta import "./locales/**/*.json" 7✓ common · checkout (en, fr)8 keys 312 created · 0 reused9 translations 624 created · 0 updated · 0 unchanged10 errors 0 · glossary 0 violations 12# non-standard layout? override the inference per file:13sonenta import strings.fr.json --language fr --namespace common Trees may be nested or flat — both import identically. --status sets the incoming status (draft or translated, default translated); --version targets a non-default version. Plurals expressed as a CLDR dict ({ one, other }) are stored as plural forms automatically.
3. Publish to the CDN
Importing fills the project; publishing makes it servable. Cut a release and the bundles propagate to the global CDN the SDK reads from.
terminal 1# cut a CDN release so the SDK and your build can fetch it2sonenta releases publish 4→ released "main" · propagating to cdn.sonenta.com Releases are immutable snapshots of a version. Re-publish whenever you import new content — the SDK and your static build both fetch the latest release.
4. Verify the bundle
Confirm the content is live. A published bundle is a plain, public JSON file per language and namespace — fetch one directly, or open the project in the dashboard.
terminal 1# the published bundle is public — no auth needed2curl -s https://cdn.sonenta.com/p/<project_uuid>/main/latest/fr/common.json 404 on a locale? It isn't one of the project's languages yet, or the release hasn't propagated — give it a few seconds and retry.
5. Point your SDK at Sonenta
Swap your i18next backend for the Sonenta provider. Your t() calls, keys and namespaces stay the same — the translations now come from the CDN bundle you just published.
main.tsx 1// src/main.tsx — point @sonenta/react-i18next at the same project2import { SonentaProvider } from "@sonenta/react-i18next"; 4<SonentaProvider5 projectUuid="<project_uuid>"6 token={import.meta.env.VITE_SONENTA_TOKEN}7 defaultLocale="fr"8 namespaces={["common", "checkout"]}9>10 <App />11</SonentaProvider> Keys that contain dots (a version, a price, a scripture reference)? Set keySeparator={false} and switch the project to flat — see the Flat vs nested keys guide. Otherwise the nested default just works.
Re-run anytime
sonenta import is idempotent: re-importing identical content changes nothing (0 created, 0 updated, N unchanged). Wire it into CI to keep Sonenta in sync with your repo — it only ever creates what's missing and updates what actually changed.
What the importer handles
- Nested or flat. Both JSON shapes import to the same keys — no pre-processing.
- Plurals. CLDR plural dicts (
{ one, other, … }, withotherrequired) become plural keys automatically. - Per-file resilience. An unknown language or namespace is reported as a per-unit error — the rest of the import still lands.
- Glossary checks. Imported translations run through your glossary; violations are listed, and skipped under strict enforcement.
- Missing-key cleanup. Open missing-key events for the imported keys auto-resolve once the values arrive.