管理面板 API:本地化(Localization)
页面摘要: 使用
registerTrads注册翻译文件,用你的插件 ID 作为键前缀以避免冲突,并在组件中使用react-intl的useIntlhook。Strapi 会自动将插件翻译与核心翻译合并。
插件可以为多种语言提供翻译,使全球用户都能使用管理界面。Strapi 会自动加载插件翻译并与核心翻译合并,使其在管理面板各处都可用。
在深入阅读本页概念之前,请确保你已经:
- 创建了一个 Strapi 插件,
- 阅读并理解了 Admin Panel API 的基础知识。
翻译文件结构
翻译文件存放在 admin/src/translations/ 目录中,每个语言区域对应 1 个 JSON 文件:
admin/src/translations/
├── en.json
├── fr.json
├── de.json
└── ...
每个翻译文件包含键值对,其中键是翻译标识符,值是已翻译的字符串:
{
"plugin.name": "My Plugin",
"plugin.description": "A custom Strapi plugin",
"settings.title": "Settings",
"settings.general": "General",
"settings.advanced": "Advanced"
}
registerTrads 函数 {#registertrads}
在带有管理面板部分的 Strapi 插件中,registerTrads() 用于加载你插件 UI 标签与消息的翻译。如果你不需要本地化,可以省略它,插件仍可运行。
registerTrads() 函数是一个异步函数,用于加载所有已配置语言区域的翻译文件。Strapi 在管理面板初始化期间调用此函数,以从所有插件收集翻译。
基础实现
JavaScript
import { prefixPluginTranslations } from './utils/prefixPluginTranslations';
import { PLUGIN_ID } from './pluginId';
export default {
register(app) {
app.registerPlugin({
id: PLUGIN_ID,
name: 'My Plugin',
});
},
async registerTrads({ locales }) {
const importedTranslations = await Promise.all(
locales.map((locale) => {
return import(`./translations/${locale}.json`)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, PLUGIN_ID),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
}),
);
return importedTranslations;
},
};
TypeScript
import { prefixPluginTranslations } from './utils/prefixPluginTranslations';
import { PLUGIN_ID } from './pluginId';
import type { StrapiApp } from '@strapi/admin/strapi-admin';
export default {
register(app: StrapiApp) {
app.registerPlugin({
id: PLUGIN_ID,
name: 'My Plugin',
});
},
async registerTrads({ locales }: { locales: string[] }) {
const importedTranslations = await Promise.all(
locales.map((locale) => {
return import(`./translations/${locale}.json`)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, PLUGIN_ID),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
}),
);
return importedTranslations;
},
};
函数参数
registerTrads 函数接收一个包含以下属性的对象:
| Parameter | Type | Description |
|---|---|---|
locales | string[] | 管理面板中配置的语言区域代码数组(例如 ['en', 'fr', 'de']) |
返回值
该函数必须返回一个解析为翻译对象数组的 Promise。每个对象具有以下结构:
{
data: Record<string, string>; // Translation key-value pairs
locale: string; // Locale code (e.g., 'en', 'fr')
}
翻译键前缀
翻译键必须以你的插件 ID 作为前缀,以避免与其他插件及核心 Strapi 翻译发生冲突。例如,如果你的插件 ID 是 my-plugin,那么 plugin.name 这样的键应变为 my-plugin.plugin.name。
使用 prefixPluginTranslations 工具函数可以自动为所有键添加前缀:
JavaScript
const prefixPluginTranslations = (trad, pluginId) => {
if (!pluginId) {
throw new TypeError("pluginId can't be empty");
}
return Object.keys(trad).reduce((acc, current) => {
acc[`${pluginId}.${current}`] = trad[current];
return acc;
}, {});
};
export { prefixPluginTranslations };
TypeScript
type TradOptions = Record<string, string>;
const prefixPluginTranslations = (
trad: TradOptions,
pluginId: string,
): TradOptions => {
if (!pluginId) {
throw new TypeError("pluginId can't be empty");
}
return Object.keys(trad).reduce((acc, current) => {
acc[`${pluginId}.${current}`] = trad[current];
return acc;
}, {} as TradOptions);
};
export { prefixPluginTranslations };
例如,如果你的翻译文件包含:
{
"plugin.name": "My Plugin",
"settings.title": "Settings"
}
在以插件 ID my-plugin 添加前缀后,它们变为:
my-plugin.plugin.namemy-plugin.settings.title
缺失的翻译文件
registerTrads 函数应通过返回该语言区域的空对象,来优雅处理缺失的翻译文件。上面示例中的 .catch() 处理器确保如果某个翻译文件不存在,插件仍会返回一个有效的翻译对象:
.catch(() => {
return {
data: {},
locale,
};
});
这样,插件只需为部分语言区域(例如仅英文)提供翻译,而不会导致其他语言区域的管理面板崩溃。
组件中的翻译
要在你的 React 组件中使用翻译,请使用 react-intl 中的 useIntl hook:
JavaScript
import { useIntl } from 'react-intl';
import { PLUGIN_ID } from '../pluginId';
const HomePage = () => {
const { formatMessage } = useIntl();
return (
<div>
<h1>
{formatMessage({
id: `${PLUGIN_ID}.plugin.name`,
defaultMessage: 'My Plugin',
})}
</h1>
<p>
{formatMessage({
id: `${PLUGIN_ID}.plugin.description`,
defaultMessage: 'A custom Strapi plugin',
})}
</p>
</div>
);
};
export default HomePage;
TypeScript
import { useIntl } from 'react-intl';
import { PLUGIN_ID } from '../pluginId';
const HomePage = () => {
const { formatMessage } = useIntl();
return (
<div>
<h1>
{formatMessage({
id: `${PLUGIN_ID}.plugin.name`,
defaultMessage: 'My Plugin',
})}
</h1>
<p>
{formatMessage({
id: `${PLUGIN_ID}.plugin.description`,
defaultMessage: 'A custom Strapi plugin',
})}
</p>
</div>
);
};
export default HomePage;
翻译键辅助函数
为了避免重复编写插件 ID 前缀,可以创建一个辅助函数:
JavaScript
import { PLUGIN_ID } from '../pluginId';
export const getTranslation = (id) => `${PLUGIN_ID}.${id}`;
TypeScript
import { PLUGIN_ID } from '../pluginId';
export const getTranslation = (id: string) => `${PLUGIN_ID}.${id}`;
然后在组件中使用它:
JavaScript
import { useIntl } from 'react-intl';
import { getTranslation } from '../utils/getTranslation';
const HomePage = () => {
const { formatMessage } = useIntl();
return (
<div>
<h1>
{formatMessage({
id: getTranslation('plugin.name'),
defaultMessage: 'My Plugin',
})}
</h1>
</div>
);
};
TypeScript
import { useIntl } from 'react-intl';
import { getTranslation } from '../utils/getTranslation';
const HomePage = () => {
const { formatMessage } = useIntl();
return (
<div>
<h1>
{formatMessage({
id: getTranslation('plugin.name'),
defaultMessage: 'My Plugin',
})}
</h1>
</div>
);
};
配置中的翻译
在配置菜单链接、设置板块以及其他管理面板元素时,也会用到翻译键:
JavaScript
export default {
register(app) {
app.addMenuLink({
to: '/plugins/my-plugin',
icon: PluginIcon,
intlLabel: {
id: 'my-plugin.plugin.name', // Prefixed translation key
defaultMessage: 'My Plugin', // Fallback if translation missing
},
Component: () => import('./pages/App'),
});
},
};
TypeScript
import type { StrapiApp } from '@strapi/admin/strapi-admin';
export default {
register(app: StrapiApp) {
app.addMenuLink({
to: '/plugins/my-plugin',
icon: PluginIcon,
intlLabel: {
id: 'my-plugin.plugin.name', // Prefixed translation key
defaultMessage: 'My Plugin', // Fallback if translation missing
},
Component: () => import('./pages/App'),
});
},
};
插件翻译生命周期
Strapi 的管理面板会自动:
- 在初始化期间为所有已注册插件调用
registerTrads - 将来自所有插件的翻译与核心 Strapi 翻译合并
- 应用来自管理面板配置的自定义翻译(如果有)
- 通过
react-intl在管理面板各处提供翻译
实际上,先加载核心管理面板翻译,再将插件翻译合并到其上;而 config.translations 中的项目级覆盖则允许你自定义管理面板中显示的标签。
最佳实践
- 始终为翻译键添加前缀。 使用
prefixPluginTranslations或手动为键添加插件 ID 前缀,以避免冲突。 - 提供默认消息。 使用
formatMessage时始终包含defaultMessage,作为翻译缺失时的兜底。 - 优雅处理缺失的翻译。
registerTrads函数应针对缺失的语言区域返回空对象,而不是抛出错误。 - 使用具有描述性的键名。 选择清晰、层级化的键名(例如
settings.general.title,而不是title1)。 - 至少支持英文。 提供英文翻译可确保你的插件开箱即用。
- 使用多个语言区域验证行为。 测试当在管理面板中选择不同语言区域时,你的插件是否能正常工作。
en 语言区域在 Strapi 中始终可用,并作为兜底语言区域。如果所选语言区域缺少翻译,Strapi 会使用英文翻译。
要查看你的 Strapi 实例中可用的语言区域,请检查 src/admin/app.ts 或 src/admin/app.js 文件中的 config.locales 数组。对于运行时的编程访问,请参阅 访问 Redux store(注意,内部 store 结构可能在不同版本间发生变化)。