Skip to main content

sfnext-i18n

Translate and localize a Storefront Next storefront with i18next: useTranslation in components, getTranslation in loaders/actions/utilities, translations.json namespaces, pluralization and interpolation, Zod validation-message factories, adding a new language or locale, extension translations, and locale/currency switching. Use when adding or editing strings, "add a Brazilian Portuguese locale", supportedLngs / fallbackLng / supportedLocales, src/locales, locales:aggregate-extensions, LocaleSwitcher, mockI18nContext, hydration mismatch on translated text, or missing-key problems. Do not use for site/locale URL prefix routing (use `storefront-next:sfnext-routing`), site and currency configuration keys (use `storefront-next:sfnext-configuration`), or extension scaffolding (use `storefront-next:sfnext-extensions`).

소스 정보

저장소
SalesforceCommerceCloud/b2c-developer-tooling
최근 소스 활동
2026년 10월 4일 16:40
감지된 SKILL.md 언어
영어
스타
56
포크
22

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
3 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
sfnext-i18n
description
Translate and localize a Storefront Next storefront with i18next: useTranslation in components, getTranslation in loaders/actions/utilities, translations.json namespaces, pluralization and interpolation, Zod validation-message factories, adding a new language or locale, extension translations, and locale/currency switching. Use when adding or editing strings, "add a Brazilian Portuguese locale", supportedLngs / fallbackLng / supportedLocales, src/locales, locales:aggregate-extensions, LocaleSwitcher, mockI18nContext, hydration mismatch on translated text, or missing-key problems. Do not use for site/locale URL prefix routing (use `storefront-next:sfnext-routing`), site and currency configuration keys (use `storefront-next:sfnext-configuration`), or extension scaffolding (use `storefront-next:sfnext-extensions`).
# Storefront Next Internationalization Storefront Next uses `i18next` with a server instance (all languages loaded) and a browser instance (loads the active language as a static JS chunk). The infrastructure comes from `@salesforce/storefront-next-runtime/i18n`; your project owns the translations in `src/locales/`. The full guide ships in your project as `docs/README-I18N.md`. Caution: its locale-detection and `set-locale` passages are outdated; trust the code and this skill. ## Which API to use | Where | API | |-------|-----| | React components | `useTranslation` from `react-i18next` | | Loaders, actions, middleware, utilities, tests | `getTranslation` from `@salesforce/storefront-next-runtime/i18n` | ```tsx import { useTranslation } from 'react-i18next'; export function AddToCart({ count }: { count: number }) { const { t } = useTranslation('product'); // namespace = top-level key in translations.json return <button>{t('addToCart', { count })}</button>; } ``` ```ts import type { LoaderFunctionArgs } from 'react-router'; import { getTranslation } from '@salesforce/storefront-next-runtime/i18n'; export function loader({ context }: LoaderFunctionArgs) { const { t } = getTranslation(context); // server: pass the router context return { title: t('product:title') }; } ``` - Without a namespace argument, prefix keys: `useTranslation()` then `t('product:title')` works. You lose key autocomplete, not correctness. - Client-only non-component code may call `getTranslation()` with no argument. - Other exports from the same module: `getLocale`, `mockI18nContext` (tests), `createI18nMiddleware`. ## File layout ``` src/locales/ ├── index.ts # barrel: explicit import of every locale ├── types.ts # DeepPartial<T> └── en-US/ ├── index.ts # merges translations.json + aggregated extension strings └── translations.json # ONE file; each top-level key is a namespace ``` The project ships 17 locale directories (`ls src/locales`). Each `translations.json` is a single file whose top-level keys (`product`, `cart`, `checkout`, `routeError`, ...) are namespaces. There is no separate per-namespace file. ```json { "product": { "addToCart": "Add to cart", "greeting": "Hello, {{name}}!", "itemCount_one": "{{count}} item", "itemCount_other": "{{count}} items" } } ``` - Interpolation uses `{{var}}`. - Plurals use i18next suffix keys (`_one`, `_other`, plus `_zero`, `_few`, ... where a language needs them), as in the shipped files. Call `t('itemCount', { count })` with the base key. Do not use nested `{ one, other }` objects. - `t()` key types come from the `en-GB` resources (`src/middlewares/i18next.server.ts`). Non-English locales are typed `satisfies DeepPartial<typeof enUS>`, so they may omit keys (i18next falls back to `fallbackLng`) but keys they include must match the English shape. To customize copy, edit `translations.json` directly. ## Add a new locale (checklist) Keep these in sync or the locale silently never appears: 1. Create `src/locales/pt-BR/index.ts` and `translations.json`. Copy an existing locale (for example `de-DE`, which carries the `DeepPartial` typing) and translate. 2. Import and register it in the barrel `src/locales/index.ts` (both the import and the exported object). 3. Add the id to `i18n.supportedLngs` in `config.server.ts` (and check `fallbackLng`). 4. Add `{ id: 'pt-BR', preferredCurrency: 'BRL' }` to the site's `supportedLocales`, and make sure that currency is in the site's `supportedCurrencies`. The shape is `commerce.sites[]`; see `storefront-next:sfnext-configuration`. 5. Add extension translations for the new locale (`src/extensions/<ext>/locales/pt-BR/translations.json`) if the extensions you use ship translations. 6. Run `pnpm locales:aggregate-extensions` (also run automatically by `pnpm dev` and `pnpm build`). 7. Verify: `pnpm dev`, then open the site under the new locale prefix and switch via the locale switcher. Details and currency rules: [references/locale-config.md](references/locale-config.md). ## How the active locale is chosen The locale is resolved from the site context, driven by the URL prefix (default `/:siteId/:localeId`, from `config.url.prefix`). `siteContextMiddleware` runs before `i18nextMiddleware` (see the middleware array in `src/root.tsx`). It is not read from a `lng` cookie or `Accept-Language`. `src/components/locale-switcher` changes locale by navigating to the same page under the rebuilt prefix (`buildUrl`, `resolvePrefix`, `stripPathPrefix` from `@salesforce/storefront-next-runtime/site-context`) and posting the choice to persist it. There is no `action.set-locale` route. Render it with `import LocaleSwitcher from '@/components/locale-switcher'`. Currency is switched independently with `src/components/currency-switcher`. ## Extension translations Each extension keeps its strings in `src/extensions/<name>/locales/<locale>/translations.json`. Its namespace is `ext` + PascalCase directory name: ```tsx const { t } = useTranslation('extStoreLocator'); // src/extensions/store-locator ``` `src/extensions/locales/<locale>/index.ts` is generated by `pnpm locales:aggregate-extensions`. Never edit it, and leave its `// @sfdc-extension-line` marker comments alone. ## Validation messages: Zod schema factories Module-level `t()` runs before i18next is initialized. Build the schema from a function: ```tsx import { useMemo } from 'react'; import { z } from 'zod'; import type { TFunction } from 'i18next'; import { useTranslation } from 'react-i18next'; export const createSchema = (t: TFunction) => z.object({ email: z.string().email(t('validation:emailInvalid')) }); function MyForm() { const { t } = useTranslation(); const schema = useMemo(() => createSchema(t as unknown as TFunction), [t]); // useForm({ resolver: zodResolver(schema) }) } ``` `createCustomerProfileFormSchema` in `src/components/customer-profile-form/index.tsx` is a working example. ## Testing translations `vitest.setup.ts` initializes i18next with all locales (language `en-GB`), so components using `useTranslation` render real English text. For loaders/actions, `createTestContext({ locale })` from `@/lib/test-utils` wires `mockI18nContext` for you; call it directly when building a context by hand. Assert on text with `getTranslation()` or literal English. Locale completeness tests (for example `src/locales/*.locale-completeness.test.ts`) are a good pattern for feature-specific keys. See `storefront-next:sfnext-testing`. ## Pitfalls | Pitfall | Fix | |---------|-----| | Importing `getTranslation` from `@/lib/i18next` | It lives in `@salesforce/storefront-next-runtime/i18n`. | | Importing `@salesforce/storefront-next-runtime/i18n/client` from a `*.server.ts` file | Browser-only; it fails to bundle and lint blocks it. | | Statically importing `@/locales` in `root.tsx` or the error boundary | Pulls every language into the client bundle; `src/locales/locale-imports.test.ts` guards this. The root loader passes `errorTranslations` (the `routeError` bundle) instead. | | New locale not showing up | Check all of: `src/locales/index.ts`, `supportedLngs`, site `supportedLocales`. | | `t()` at module scope | Use a factory or call inside the component/function. | | Hydration mismatch on translated text | `src/i18n-client-init.ts` gates hydration with `whenI18nReady()`; do not bypass it. | | Same key name in two namespaces | Use `t('ns:key')` to disambiguate. | | "Click here" style link text | `jsx-a11y/anchor-ambiguous-text` cannot see translated strings; review link copy in JSON (see `storefront-next:sfnext-accessibility`). | ## Finding more - `docs/README-I18N.md` (architecture, currency flow, using a different i18n library) and the doc index in `AGENTS.md`. - `b2c docs search "i18n"` (or the MCP `docs_search` tool) for the Storefront Next docs corpus. ## Related Skills - `storefront-next:sfnext-configuration` - `i18n`, `supportedLocales`, `supportedCurrencies` config - `storefront-next:sfnext-routing` - site/locale URL prefix and site-aware links - `storefront-next:sfnext-extensions` - extension structure and `ext*` namespaces - `storefront-next:sfnext-components` - using translations in UI components - `storefront-next:sfnext-testing` - unit and story tests - `storefront-next:sfnext-accessibility` - translated link and label text
GitHub에서 보기