Skip to content

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.

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 returns 403. 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).

Install the CLI globally, scaffold a config that points at your project, and export your key.

Fenêtre de terminal
# one global install: gives you the `sonenta` command (Node >= 18)
npm i -g @sonenta/cli
# scaffold sonenta.config.json and point it at your project
sonenta init --project <project_uuid>
# the CLI talks to the MCP surface, use an mcp:* scoped key
export 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.

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>.json
locales/
├─ 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
Fenêtre de terminal
# preview first: no writes, prints exactly what WOULD change
sonenta import "./locales/**/*.json" --dry-run
# the real run: idempotent, safe to repeat
sonenta 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 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.

Importing fills the project; publishing makes it servable. Cut a release and the bundles propagate to the global CDN the SDK reads from.

Fenêtre de terminal
# cut a CDN release so the SDK and your build can fetch it
sonenta releases publish
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.

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.

Fenêtre de terminal
# the published bundle is public: no auth needed
curl -s https://cdn.sonenta.com/p/<project_uuid>/main/latest/fr/common.json

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 project
import { SonentaProvider } from "@sonenta/react-i18next";
<SonentaProvider
projectUuid="<project_uuid>"
token={import.meta.env.VITE_SONENTA_TOKEN}
defaultLocale="fr"
namespaces={["common", "checkout"]}
>
<App />
</SonentaProvider>

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.

  • Nested or flat. Both JSON shapes import to the same keys, no pre-processing.
  • Plurals. CLDR plural dicts ({ one, other, … }, with other required) 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.