Guidelines for internationalizing a react-admin or shadcn admin kit application. Use when adding i18n support, creating translation files, translating hardcoded English strings in JSX, or reviewing i18n PRs in a react-admin project. Covers namespace conventions, interpolation, pluralization, locale-aware formatting, and the full workflow for converting an English-only app to multi-language. Trigger this skill whenever the user mentions i18n, internationalization, translation, localization, locale switching, or multi-language in a react-admin or shadcn admin kit context — even if they just say "translate this component" or "add French support".
This skill describes how to add i18n support to a react-admin / shadcn admin kit application. It covers the full workflow: setting up the i18n provider, structuring translation catalogs, converting hardcoded English strings in JSX to translation keys, and avoiding the many pitfalls that make this task tedious.
When converting an English-only react-admin app to multi-language, follow this order:
ra-i18n-polyglot, ra-language-english, ra-language-<target>, and any auth-provider language packs (e.g. ra-supabase-language-french).polyglotI18nProvider with merged catalogs and browser locale detection.satisfies CrmMessages) so missing keys fail at compile time.<Translate> or calls or rely on react-admin's auto-inference.useTranslate()translate="no" and <meta name="google" content="notranslate" /> to your HTML shell.useLocaleState() and useLocales()._ defaults, and duplicate labels.React-admin's translation system (Polyglot.js) uses a hierarchical key structure. Respect these namespaces:
ra.* — Framework-level messages (auth, navigation, pagination, CRUD notifications, validation). Never override unless you need to customize framework wording.ra-supabase.* (or other auth provider namespaces) — Auth-provider-specific messages. You can override individual keys via a separate override object merged on top.resources.* — Resource and field names. This is the default namespace react-admin uses to infer labels in forms, show views, datagrid columns, and filters. Structure:
resources.<resourceName>.name — singular/plural resource label (with |||| for pluralization)resources.<resourceName>.fields.<fieldName> — field labelresources.<resourceName>.empty / resources.<resourceName>.invite — custom empty-state messages (optional, react-admin falls back to ra.page.empty / ra.page.invite)app.* (or a project-specific prefix like crm.*) — Application-specific copy: empty states, helper text, tooltips, dashboard titles, domain wording. Everything that is not a resource field name and not a framework message goes here.resources.* MattersWhen you place field translations under resources.<resource>.fields.<field>, react-admin automatically resolves labels for <TextInput source="first_name" />, <TextField source="first_name" />, and filter inputs — without passing an explicit label prop. This eliminates an entire class of duplication bugs.
forcedCaseName PatternSome languages (like French) don't capitalize common nouns the same way English does. React-admin's ra.page.empty interpolates %{name} which comes from resources.<resource>.name, but pluralization via |||| can produce awkward casing in sentences. Use a dedicated forcedCaseName key when you need a specific casing for interpolation contexts: