Localization
Puck’s default UI uses US English. Use the dictionary prop to translate or reword Puck-owned strings.
See the Dictionary API reference for the full list of strings and their defaults.
Translating Puck strings
To translate strings, provide a dictionary object with the keys you want to change and their corresponding translations.
import { Puck } from "@puckeditor/core";
export function Editor() {
return (
<Puck
dictionary={{
"header-publish": "Publicar", // "Publish" button
"field-richtext-bold": "Negrita", // "Bold" rich text control
"viewport-switch": "Cambiar a {label}", // Viewport tooltip
}}
config={config}
data={data}
/>
);
}Switching languages at runtime
Puck reacts to any changes in the dictionary prop, so you can swap the object at runtime to change the language of the UI.
import { useState } from "react";
import { Puck } from "@puckeditor/core";
const dictionaries = {
en: {},
es: { "header-publish": "Publicar" },
};
export function Editor() {
const [locale, setLocale] = useState<"en" | "es">("en");
return (
<>
<button onClick={() => setLocale(locale === "en" ? "es" : "en")}>
Toggle language
</button>
<Puck dictionary={dictionaries[locale]} config={config} data={data} />
</>
);
}If you already use a library like react-i18next or next-intl, resolve your strings into a plain object and feed that object into dictionary.
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
export function Editor() {
const { t } = useTranslation();
const dictionary = useMemo(
() => ({
"header-publish": t("header-publish"),
"field-richtext-bold": t("field-richtext-bold"),
}),
[t]
);
return <Puck dictionary={dictionary} config={config} data={data} />;
}Translating user-defined labels
The dictionary only covers strings owned by the editor. Labels defined by your app should be translated where they are configured:
- Custom plugin labels via the plugin
label - Viewport labels via the
viewports.labelprop - Component labels, root label, category titles and field labels via the
configprop
Plugins
Your own plugins define their own label; translate it before passing the plugin to Puck.
import { useMemo } from "react";
import { Puck } from "@puckeditor/core";
export function Editor({ locale }) {
const labels = translations[locale];
const plugins = useMemo(
() => [
{
name: "my-plugin",
icon: <SomeIcon />,
label: labels.plugins.myPlugin,
render: () => <MyPanel />,
},
],
[labels]
);
return <Puck config={config} data={data} plugins={plugins} />;
}Viewports
Viewport names are labels in the viewports prop.
import { useMemo } from "react";
import { Puck, type Viewports } from "@puckeditor/core";
export function Editor({ locale }) {
const labels = translations[locale];
const viewports: Viewports = useMemo(
() => [
{ width: 360, icon: "Smartphone", label: labels.viewports.small },
{ width: 768, icon: "Tablet", label: labels.viewports.medium },
{ width: 1280, icon: "Monitor", label: labels.viewports.large },
],
[labels]
);
return <Puck config={config} data={data} viewports={viewports} />;
}Config
Component labels, root label, category titles and field labels come from your config prop. Update the config when the locale changes, in the same way you update the dictionary.
import { useMemo } from "react";
import { Puck } from "@puckeditor/core";
export function Editor() {
const labels = translations["en"];
const localizedConfig = useMemo(
() => {
components: {
HeadingBlock: {
label: labels.components.HeadingBlock,
fields: {
title: { label: labels.fields.title },
},
render: (props) => <h1>{props.title}</h1>,
},
}
},
[labels]
);
return <Puck config={localizedConfig} data={data} />;
}Interpolating values
Some messages have dynamic values that can be interpolated into the string.
Use {key} in the string to interpolate a value supplied by Puck.
// Interpolates the viewport `label` value into the string.
<Puck dictionary={{ "viewport-switch": "Cambiar a {label}" }} />See the Dictionary API reference for the full list of strings that support interpolation and their corresponding keys.
Hiding a string
Set a key to an empty string to render nothing for that message.
// Hides the page label entirely.
<Puck dictionary={{ "label-page": "" }} />Custom strings
Puck supports adding your own keys to the dictionary to use them through the internal Puck API.
import { Puck, createUsePuck } from "@puckeditor/core";
const usePuck = createUsePuck();
const HeaderActions = ({ children }) => {
const savedMsg = usePuck((s) => s.dictionary["saved-state"]);
return (
<div>
<span>{savedMsg}</span>
{children}
</div>
);
};
export function Editor() {
return (
<Puck
dictionary={{ "saved-state": "Saved" }}
overrides={{
fieldTypes: {
headerActions: { render: HeaderActions },
},
}}
config={config}
data={data}
/>
);
}