Schnellstart
React + i18next
Installiere das SDK, packe deine App in <SonentaProvider /> und rufe useTranslation() auf. Fehlende Keys landen automatisch im Dashboard, ohne Zusatzaufwand.
1. Installieren
Eine einzige Abhängigkeit. Kein peer-dep-Akrobatik, das SDK bringt alles React-seitig mit.
terminal 1npm i @sonenta/react-i18next 2. App umschließen
SonentaProvider erwartet eine projectUuid und eine defaultLocale; Ihr API-Schlüssel gehört in die Prop token. Es gibt keine automatische Locale-Erkennung, defaultLocale ist erforderlich. Namespaces werden bei Bedarf vom CDN geladen, und fehlende Schlüssel werden gebündelt und alle 5 Sekunden oder alle 50 Ereignisse gesendet, je nachdem, was zuerst eintritt.
main.tsx 1// src/main.tsx2import { SonentaProvider } from "@sonenta/react-i18next";3import { createRoot } from "react-dom/client";4import { App } from "./App"; 6createRoot(document.getElementById("root")!).render(7 <SonentaProvider8 projectUuid="proj_xxx"9 token={import.meta.env.VITE_SONENTA_TOKEN}10 defaultLocale="en"11 namespaces={["common"]}12 >13 <App />14 </SonentaProvider>15); Alle SonentaProvider-Props
| Prop | Typ | Default |
|---|---|---|
| projectUuid | string | erforderlich |
| defaultLocale | Locale | erforderlich |
| children | ReactNode | erforderlich |
| token | string | req. unless transport |
| namespaces | Namespace[] | ["common"] |
| defaultNS | Namespace | - |
| keySeparator | string | false | "." (auto-detected) |
| nsSeparator | string | false | ":" |
| apiBase | string | https://api.sonenta.dev |
| cdnBase | string | https://cdn.sonenta.com |
| fetchImpl | typeof fetch | global fetch |
| env | "prod" | "dev" | "prod" |
| version | string | "main" |
| versionSlug (deprecated) | string | - |
| missingHandler | "send" | "log" | "off" | "send" |
| transport | (batch: MissingKeyEvent[]) => … | built-in POST |
| flushIntervalMs | number | 5000 |
| flushBatchSize | number | 50 |
| missingEventsBufferSize | number | 200 |
| initialBundles | Record<Locale, Record<...>> | - |
| languageCatalog | LanguageMeta[] | - |
| disableLanguageCatalog | boolean | false |
| disableLanguageManifest | boolean | false |
| fallbackLng | Locale | Locale[] | - |
| surface | Surface | - |
| surfaceBreakpoints | SurfaceBreakpoints | boolean | - |
| a11ySurfaces | A11ySurface[] | [] |
| plainLanguage | boolean | false |
| plugins | SonentaPlugin[] | - |
| interpolation | { format?: (…) => string } | - |
-
tokenis required unless you supply your owntransport. Passing neither throws at mount. It authenticates only the missing-key POST, the key-style probe, and the runtime fetch inenv: "dev". - Only
formatis configurable underinterpolation.escapeValueis alwaysfalse, because React escapes already. - Regional variants already fall back to their base (
fr-CAtofr) with no configuration.fallbackLngis appended after that chain, it does not replace it.
3. Hook nutzen
useTranslation() gibt { t, i18n } zurück. Vertraute Form, falls du react-i18next kennst. i18n.ready zeigt an, wann die initialen Namespaces hydriert sind; i18n.changeLanguage() wechselt die Locale zur Laufzeit.
Checkout.tsx 1// src/Checkout.tsx2import { useTranslation } from "@sonenta/react-i18next"; 4export function Checkout() {5 const { t, i18n } = useTranslation("common"); 7 if (!i18n.ready) return null; // first paint after hydration 9 return (10 <button onClick={() => i18n.changeLanguage("fr")}>11 {t("checkout.review.confirm")}12 </button>13 );14} Was du gratis bekommst
- Missing-Key-Erfassung. Jeder Key, den du aufrufst und der nicht im Dictionary ist, wird in die Queue gestellt, debounced (Default 5s) und in die Missing-Queue deines Dashboards per POST gesendet. Production-safe, dein Fallback rendert weiter.
- Namespaces vom CDN. Übersetzungs-Bundles werden von
cdn.sonenta.commit HTTP-Caching und stale-while-revalidate gezogen. Kein Build-Time-Bundling nötig. - Automatische Locale-Erkennung. Übergibst du keine
defaultLocale, liest das SDKnavigator.languageund fällt auf den Default deines Projekts zurück. - Offene Exports. Alles, was du nach Sonenta pushst, kannst du als JSON i18next, XLIFF oder PO zurückexportieren. Morgen das Tool wechseln, ohne Code umzuschreiben.
Three failures that stay silent
Each of these leaves your page looking perfectly fine. None of them raises an error you will notice.
- A broken token barely warns, and never throws. Passing no
tokenand notransportthrows at mount. A token that is present but shaped like an unset environment variable ("undefined","null") produces aconsole.warn. Both guards exist only from@sonenta/react-i18next2.6.1 (@sonenta/i18n-core1.1.3); below that, nothing is reported at all. And an empty string, the?? ""you write to satisfy TypeScript, is covered by no warning at any version. In every case the server returns 401, the SDK degrades gracefully, the CDN keeps serving, and your app stays unauthenticated. Do not grep your logs forApiKey undefined: with?? ""that is a guaranteed false negative. Read theAuthorizationheader of the network request in your deployed build instead. - A missing namespace looks like an empty one. Namespace bundles are fetched from the CDN only when
envis"prod"; in"dev"they come from the authenticated runtime API instead. And a 404 resolves to an empty bundle, with no error and no retry, so a namespace you have not published yet is indistinguishable from one that is genuinely empty. - Undeclared a11y surfaces fall back quietly. The accessibility accessors only read surfaces you asked for at load time, via
a11ySurfaces. Without it the overlay is never downloaded andt.aria(key)simply returns the visible text, with no error and no warning. The symptom is not a crash, it is anaria-labelthat duplicates the label next to it.
Eigener Transport (fortgeschritten)
Willst du fehlende Keys in deinen eigenen Observability-Stack loggen, hinter deiner Auth abschotten oder in Tests stubben? Übergib eine transport-Funktion. Das SDK debounced und batched weiterhin; du entscheidest, was mit dem Batch passiert.
main.tsx 1// custom transport, useful for tests, edge cases, or auditing2<SonentaProvider3 projectUuid="proj_xxx"4 token={import.meta.env.VITE_SONENTA_TOKEN}5 flushIntervalMs={2000}6 transport={(batch) => fetch("/internal/i18n-misses", {7 method: "POST",8 body: JSON.stringify(batch),9 })}10/>