快速上手
React + i18next
安装 SDK,把应用包在 <SonentaProvider /> 里,然后调用 useTranslation()。缺失的键会自动汇总到你的 dashboard, 无需额外接线。
1. 安装
单一依赖。无需折腾 peer-dep, React 侧需要的,SDK 都已包含。
terminal 1npm i @sonenta/react-i18next 2. 包装你的应用
SonentaProvider 接收 projectUuid 和 defaultLocale;API 密钥通过 token 属性传入。没有 locale 自动检测,defaultLocale 为必填。namespace 从 CDN 按需加载,缺失键会批量发送,每 5 秒或每 50 个事件触发一次,以先到者为准。
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); SonentaProvider 全部 props
| Prop | 类型 | 默认值 |
|---|---|---|
| projectUuid | string | 必填 |
| defaultLocale | Locale | 必填 |
| children | ReactNode | 必填 |
| 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
useTranslation() 返回 { t, i18n }。如果你用过 react-i18next,这个形态会很熟悉。i18n.ready 告诉你初始 namespace 何时已完成 hydrate;i18n.changeLanguage() 在运行时切换 locale。
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} 开箱即得
- 缺失键捕获。 你调用的任何不在字典里的键都会被入队,debounce(默认 5 秒)后 POST 到 dashboard 的 missing 队列。生产可用, 你的 fallback 仍会渲染。
- CDN 提供 namespace。 翻译 bundle 从
cdn.sonenta.com拉取,带 HTTP 缓存与 stale-while-revalidate。无需构建时打包。 - Locale 自动检测。 如果你不传
defaultLocale,SDK 会读取navigator.language,并在失败时回退到项目默认值。 - 开放导出。 你 push 到 Sonenta 的内容,都可以导出回 JSON i18next、XLIFF 或 PO。明天换工具,不用重写代码。
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.
自定义 transport(进阶)
想把缺失键打到自己的可观测性栈、放在自有鉴权后,或在测试里打桩?传一个 transport 函数即可。SDK 仍会做 debounce 和批量;批次怎么处理由你决定。
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/>