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 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
Section titled “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
Section titled “1. Install and initialise”Install the CLI globally, scaffold a config that points at your project, and export your key.
# one global install: gives you the `sonenta` command (Node >= 18)npm i -g @sonenta/cli
# scaffold sonenta.config.json and point it at your projectsonenta init --project <project_uuid>
# the CLI talks to the MCP surface, use an mcp:* scoped keyexport SONENTA_TOKEN=snt_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.
2. Preview, then import
Section titled “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.
# the importer infers (language, namespace) from each path:# <lang>/<namespace>.jsonlocales/├─ en/│ ├─ common.json → language en · namespace common│ └─ checkout.json → language en · namespace checkout└─ fr/ ├─ common.json → language fr · namespace common └─ checkout.json → language fr · namespace checkout# preview first: no writes, prints exactly what WOULD changesonenta import "./locales/**/*.json" --dry-run
# the real run: idempotent, safe to repeatsonenta import "./locales/**/*.json"
✓ common · checkout (en, fr) keys 312 created · 0 reused translations 624 created · 0 updated · 0 unchanged errors 0 · glossary 0 violations
# non-standard layout? override the inference per file:sonenta import strings.fr.json --language fr --namespace commonTrees 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
Section titled “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.
# cut a CDN release so the SDK and your build can fetch itsonenta releases publish
→ released "main" · propagating to cdn.sonenta.comReleases 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
Section titled “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.
# the published bundle is public: no auth neededcurl -s https://cdn.sonenta.com/p/<project_uuid>/main/latest/fr/common.json5. Point your SDK at Sonenta
Section titled “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.
// src/main.tsx: point @sonenta/react-i18next at the same projectimport { SonentaProvider } from "@sonenta/react-i18next";
<SonentaProvider projectUuid="<project_uuid>" token={import.meta.env.VITE_SONENTA_TOKEN} defaultLocale="fr" namespaces={["common", "checkout"]}> <App /></SonentaProvider>Re-run anytime
Section titled “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
Section titled “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.
- CLI: Reference. Every command, flag and the mcp:* key requirement.
- Flat vs nested keys: Guide. Keep dotted keys from breaking after import.
- Update source values: Guide. Change a key's source text after it exists.