Translation
Localize the admin with createI18n and message catalogs.
Chelone localizes through a small i18n instance you build with createI18n and
hand to <Admin i18n={…}>. A catalog is a flat map of keys to strings; the
components translate ra.* keys (page titles, pagination, bulk-action labels,
booleans, auth) and the core controllers emit chelone.* keys (save
notifications, validation). <Admin> works without an i18n instance; see
Without i18n.
Building the i18n instance
import { createI18n, defaultMessages, defineMessages } from "@chelone/core";
const en = defineMessages({
...defaultMessages, // the chelone.* core keys
"ra.action.save": "Save",
"ra.action.create": "Create",
"ra.page.list": "{name}",
"ra.boolean.true": "Yes",
"ra.boolean.false": "No",
});
export function makeI18n() {
return createI18n({
locale: "en",
messages: { en },
fallbackLocale: "en",
});
}defineMessages is an identity helper that gives you key/value type-checking.
What ships in defaultMessages
defaultMessages holds only the chelone.* keys the core controllers emit
(notifications, validation, auth and loading messages). No ra.* English
catalog ships. Spread defaultMessages first, then write the ra.* labels
your app needs yourself.
The ra.* keys are the ones the components pass to translate. Most call it
with an English default, so a missing key still reads as English (see
Missing keys). The keys, by group:
ra.action.*:add,add_filter,back,bulk_actions,bulk_edit_apply,cancel,clear_input_value,clear_search,close_menu,confirm,create,delete,edit,export,list,move_down,move_up,open_menu,refresh,remove,remove_all_filters,remove_filter,save,search,select_all,select_row,show,toggle_theme,undo,unselect,updatera.auth.*:logout,sign_in,sign_in_error,user_menura.boolean.*:true,false,nullra.input.*:file.upload_single,file.upload_several,image.upload_single,image.upload_several,password.toggle_hidden,password.toggle_visiblera.message.*:access_denied,auth_error,authentication_error,bulk_delete_content,bulk_delete_title,bulk_edit_description,bulk_edit_nested_source,bulk_update_content,bulk_update_title,delete_content,delete_title,error,loadingra.navigation.*:next,no_results,page_range_info,page_rows_per_page,partial_page_range_info,previousra.page.*:access_denied,authentication_error,create,dashboard,error,list,loadingra.saved_queries.*:new_label,query_name,remove_label_with_namera.sort.*:sort_byra.theme.*:dark,light,system
The list is read from the components, so it can grow. To check for a key,
search the source for translate("ra..
Interpolation and plurals
Chelone uses single-brace interpolation — {name} — not react-admin's
%{name}:
"ra.page.create": "Create {name}",
"ra.navigation.page_range_info": "{offsetBegin}-{offsetEnd} of {total}",Plurals are an object keyed by Intl.PluralRules categories (not react-admin's
a |||| b). other is required:
"ra.action.bulk_actions": {
one: "1 item selected",
other: "{count} items selected",
},The form is picked from the count param, or smart_count (the name
react-admin and the components use) when count is absent. Whichever you pass
also fills {count} and {smart_count} in the text:
i18n.translate("ra.action.bulk_actions", { count: 1 }); // "1 item selected"
i18n.translate("ra.action.bulk_actions", { count: 4 }); // "4 items selected"On an instance typed from a defineMessages catalog, a plural message requires
count. smart_count is accepted only through useTranslate() and the
catalog-erased instance <Admin> holds, which is how the components call it.
Without a numeric count, the other form is used. A category the locale needs
but the message lacks (for example few in Polish) falls back to other.
Missing keys never throw
If a key isn't in the catalog, translate returns the key itself (or a _
string fallback) — it never throws. A _ fallback is interpolated with the same
params, and most components pass one, so labels such as "Cancel" or "Refresh"
read as English without a catalog entry:
translate("ra.action.cancel", { _: "Cancel" }); // "Cancel" when the key is missingWhere a component passes no _, a partial catalog shows the raw
ra.page.list-style key. Add the keys you see.
Without i18n
i18n is optional on <Admin>. Without it, useTranslate() returns an echo:
a key with no _ default comes back as the key, and a key with one renders the
default with placeholders filled in. Components that pass English defaults read
as English; the rest show raw keys. In React, the built-in <LoginPage> is the exception:
it supplies its own English catalog (defaultMessages) and notification store
for its subtree, so <Admin requireAuth> shows a working form with no
i18n or notification setup. Pass i18n to <Admin> to localize it.
Multiple locales
Register more catalogs under messages and switch at runtime:
createI18n({
locale: "en",
messages: { en, fr },
fallbackLocale: "en",
});Read and change the active locale from your components:
import { useTranslate, useLocale, useSetLocale } from "@chelone/react";
const translate = useTranslate(); // translate("ra.action.save")
const locale = useLocale(); // "en"
const setLocale = useSetLocale(); // setLocale("fr")Translatable records
Catalogs localize the interface. To localize the data, a translatable record
stores { en, fr } under each translated field, for example
{ name: { en: "Chair", fr: "Chaise" } }. Edit those fields with
TranslatableInputs and show them with
TranslatableFields. Both take the list of
locales to show; the app locale only picks the tab selected first.
Tips
setLocalethrows on an unregistered locale — register every locale inmessagesbefore switching to it.- Keep the interpolation single-brace (
{name}) — porting react-admin catalogs means rewriting%{name}→{name}anda |||| b→{ one, other }.