Skip to content
Sonenta

Guide · Key nesting

Flat vs nested keys

By default Sonenta splits your keys on . into a nested JSON tree in the CDN bundle — the classic i18next shape. That is perfect for organised keys like checkout.review.confirm, but it silently mangles keys whose text contains a dot. This guide covers the project setting and the matching SDK option so your lookups always resolve.

The dotted-key trap

A key is just a string. When that string contains a literal dot — a version like App Version 6.3.8, a price, a filename, a scripture reference like Jean 3.16 — splitting on . turns one key into an accidental nested object. The translation is still stored, but t("App Version 6.3.8") no longer finds it.

bundle.json
1// your source key — a literal label with dots in it2{ "App Version 6.3.8": "App Version 6.3.8" } 4// nested bundle (default) — split on "." → broken tree5{ "App Version 6": { "3": { "8": "App Version 6.3.8" } } } 7// flat bundle — the key stays literal, lookups just work8{ "App Version 6.3.8": "App Version 6.3.8" }

The project setting

Two project-level settings control how the CDN bundle is shaped. The defaults reproduce the current i18next behaviour, so existing projects are unaffected until you opt in.

Setting Values Default
bundle_key_style nested | flat nested
bundle_key_separator string "."

Set them on the project — in your dashboard's project settings, or via the projects API. The chosen style is baked into every release, and every published version reports it back, so any client can self-configure.

versions/main
1# the version object reports the active key style2GET /v1/projects/<project_uuid>/versions/main 4{ "slug": "main", "key_style": "flat", "key_separator": "." }

Choosing flat or nested

bundle_key_style: flat

Keys are never split — each one is stored and looked up literally. Choose this when your keys contain dots or are natural text: versions, prices, filenames, scripture or legal references. App Version 6.3.8 stays exactly that.

bundle_key_style: nested

Keys are split on the separator into a JSON tree — the classic i18next layout. Choose this for deliberately namespaced keys like checkout.review.confirm. This is the default.

Match it in the SDK

@sonenta/react-i18next (>= 0.11.0) takes a keySeparator option: false for literal / flat lookups, a string for nested, default ".". There is also nsSeparator (default ":"). The SDK is literal-first — it tries an exact bundle[key] match before any splitting, so dotted keys resolve even in nested mode. On start() it also auto-detects key_style / key_separator from the published version (best-effort).

main.tsx
1// src/main.tsx — match the bundle in @sonenta/react-i18next >= 0.11.02import { SonentaProvider } from "@sonenta/react-i18next"; 4<SonentaProvider5  projectUuid="<project_uuid>"6  token={import.meta.env.VITE_SONENTA_TOKEN}7  keySeparator={false}   // literal lookup — for dotted / natural-text keys8  nsSeparator=":"        // default; set false to disable ns parsing too9>10  <App />11</SonentaProvider> 13// then t() treats the whole string as one key — no splitting14t("App Version 6.3.8");   // ✓ exact match

Auto-detection reads the version metadata and needs a key with project:read. If that read is denied (403) the SDK falls back gracefully to its defaults — so when in doubt, set keySeparator explicitly to match your bundle rather than relying on detection.

Recommendation

  • Keys contain dots? Set bundle_key_style: flat on the project and keySeparator={false} in the SDK. Both ends literal — no surprises.
  • Cleanly namespaced keys (checkout.review.confirm)? Keep the nested default; nothing to change.
  • Migrating an existing app? The defaults preserve your current behaviour. Flip to flat only when you hit a dotted key, then re-publish and update the SDK option together.

Next