Guide · Migration
Migrate from i18next
Already shipping with i18next? Bring your existing locales/ JSON into Verbumia with one command — then keep your code exactly as it is. verbumia 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 `verbumia` command (Node >= 18)2npm i -g @verbumia/cli 4# scaffold verbumia.config.json and point it at your project5verbumia init --project <project_uuid> 7# the CLI talks to the MCP surface — use an mcp:* scoped key8export VERBUMIA_TOKEN=vrb_live_<prefix>.<secret> verbumia init writes a verbumia.config.json you can commit. In CI, skip init and pass --project plus the VERBUMIA_TOKEN environment variable.
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 change2verbumia import "./locales/**/*.json" --dry-run 4# the real run — idempotent, safe to repeat5verbumia 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:13verbumia 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 it2verbumia releases publish 4→ released "main" · propagating to cdn.verbumia.ca 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.verbumia.ca/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 Verbumia
Swap your i18next backend for the Verbumia 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 @verbumia/react-i18next at the same project2import { VerbumiaProvider } from "@verbumia/react-i18next"; 4<VerbumiaProvider5 projectId="<project_uuid>"6 apiKey={import.meta.env.VITE_VERBUMIA_KEY}7 defaultLocale="fr"8 namespaces={["common", "checkout"]}9>10 <App />11</VerbumiaProvider> 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
verbumia import is idempotent: re-importing identical content changes nothing (0 created, 0 updated, N unchanged). Wire it into CI to keep Verbumia 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.