Skip to main content

管理面板 API:本地化

🌐 Admin Panel API: Localization

Page summary:

使用 registerTrads 注册翻译文件,使用你的插件 ID 作为键前缀以避免冲突,并在组件中使用 react-intluseIntl 钩子。Strapi 会自动将插件翻译与核心翻译合并。

🌐 Register translation files with registerTrads, prefix keys with your plugin ID to avoid conflicts, and use react-intl's useIntl hook in components. Strapi merges plugin translations with core translations automatically.

插件可以为多种语言提供翻译,使管理界面对全球用户可访问。Strapi 会自动加载并合并插件翻译与核心翻译,使它们在整个管理面板中可用。

🌐 Plugins can provide translations for multiple languages to make the admin interface accessible to users worldwide. Strapi automatically loads and merges plugin translations with core translations, making them available throughout the admin panel.

Prerequisites

在深入了解本页的概念之前,请确保你已经:

🌐 Before diving deeper into the concepts on this page, please ensure you have:

翻译文件结构

🌐 Translation file structure

翻译文件存储在 admin/src/translations/ 目录中,每个语言环境有一个 JSON 文件:

🌐 Translation files are stored in the admin/src/translations/ directory, with 1 JSON file per locale:

admin/src/translations/
├── en.json
├── fr.json
├── de.json
└── ...

每个翻译文件包含键值对,其中键是翻译标识符,值是翻译后的字符串:

🌐 Each translation file contains key-value pairs where keys are translation identifiers and values are the translated strings:

admin/src/translations/en.json
{
"plugin.name": "My Plugin",
"plugin.description": "A custom Strapi plugin",
"settings.title": "Settings",
"settings.general": "General",
"settings.advanced": "Advanced"
}

registerTrads 函数

🌐 The registerTrads function

在具有管理部分的 Strapi 插件中,registerTrads() 用于加载插件的 UI 标签和消息的翻译。如果你不需要本地化,你可以省略它,插件仍然可以运行。

🌐 In Strapi plugins that have an admin part, registerTrads() is used to load translations for your plugin's UI labels and messages. If you don't need localization, you can omit it and the plugin can still run.

registerTrads() 函数是一个异步函数,用于加载所有配置的语言环境的翻译文件。Strapi 在管理面板初始化期间调用此函数,以收集所有插件的翻译。

🌐 The registerTrads() function is an async function that loads translation files for all configured locales. Strapi calls this function during admin panel initialization to collect translations from all plugins.

基本实现

🌐 Basic implementation

admin/src/index.js
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;
},
};

函数参数

🌐 Function parameters

registerTrads 函数接收一个具有以下属性的对象:

🌐 The registerTrads function receives an object with the following property: | 参数 | 类型 | 描述 ||---|---|---|| locales | string[] | 在管理面板中配置的区域设置代码数组(例如,['en', 'fr', 'de']) |

返回值

🌐 Return value

该函数必须返回一个 Promise,该 Promise 解析为翻译对象数组。每个对象具有以下结构:

🌐 The function must return a Promise that resolves to an array of translation objects. Each object has the following structure:

{
data: Record<string, string>; // Translation key-value pairs
locale: string; // Locale code (e.g., 'en', 'fr')
}

翻译键前缀

🌐 Translation key prefixing

Caution

翻译键必须以你的插件 ID 为前缀,以避免与其他插件和 Strapi 核心翻译的冲突。例如,如果你的插件 ID 是 my-plugin,那么像 plugin.name 这样的键应该变为 my-plugin.plugin.name

🌐 Translation keys must be prefixed with your plugin ID to avoid conflicts with other plugins and core Strapi translations. For example, if your plugin ID is my-plugin, a key like plugin.name should become my-plugin.plugin.name.

使用 prefixPluginTranslations 工具函数自动为所有键添加前缀:

🌐 Use the prefixPluginTranslations utility function to automatically prefix all keys:

admin/src/utils/prefixPluginTranslations.js
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 };

例如,如果你的翻译文件包含:

🌐 For instance, if your translation file contains:

{
"plugin.name": "My Plugin",
"settings.title": "Settings"
}

在前缀添加插件 ID my-plugin 后,这些变为:

🌐 After prefixing with plugin ID my-plugin, these become:

  • my-plugin.plugin.name
  • my-plugin.settings.title

缺少翻译文件

🌐 Missing translation files

registerTrads 函数应该通过为该语言环境返回一个空对象来优雅地处理缺失的翻译文件。上面的示例中的 .catch() 处理程序确保即使翻译文件不存在,插件仍然返回一个有效的翻译对象:

🌐 The registerTrads function should gracefully handle missing translation files by returning an empty object for that locale. The .catch() handler in the example above ensures that if a translation file does not exist, the plugin still returns a valid translation object:

.catch(() => {
return {
data: {},
locale,
};
});

这允许插件只为某些语言环境提供翻译(例如,仅英语),而不会破坏其他语言环境的管理面板。

🌐 This allows plugins to provide translations for only some locales (e.g., only English) without breaking the admin panel for other locales.

组件中的翻译

🌐 Translations in components

要在你的 React 组件中使用翻译,请使用来自 react-intluseIntl 钩子:

🌐 To use translations in your React components, use the useIntl hook from react-intl:

admin/src/pages/HomePage.jsx
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;

翻译键的辅助函数

🌐 Helper function for translation keys

为了避免重复插件 ID 前缀,请创建一个辅助函数:

🌐 To avoid repeating the plugin ID prefix, create a helper function:

admin/src/utils/getTranslation.js
import { PLUGIN_ID } from '../pluginId';

export const getTranslation = (id) => `${PLUGIN_ID}.${id}`;

然后在组件中使用它:

🌐 Then use it in components:

admin/src/pages/HomePage.jsx
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>
);
};

配置中的翻译

🌐 Translations in configuration

在配置菜单链接、设置部分以及其他管理面板元素时,也会使用翻译键:

🌐 Translation keys are also used when configuring menu links, settings sections, and other admin panel elements:

admin/src/index.js
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: async () => {
const { App } = await import('./pages/App');
return App;
},
});
},
};

插件翻译生命周期

🌐 Plugin translation lifecycle

Strapi 的管理面板自动:

🌐 Strapi's admin panel automatically:

  1. 在初始化期间调用所有已注册插件的 registerTrads
  2. 将所有插件的翻译与核心 Strapi 翻译合并
  3. 应用管理员配置的自定义翻译(如果有的话)
  4. 通过 react-intl 在整个管理面板中提供翻译

在实际操作中,核心管理翻译首先加载,插件翻译会在其上合并,而 config.translations 中的项目级覆盖允许你自定义在管理面板中显示的标签。

🌐 In practice, core admin translations are loaded first, plugin translations are merged on top, and project-level overrides in config.translations let you customize the labels displayed in the admin panel.

最佳实践

🌐 Best practices

  • 始终为翻译键添加前缀。 使用 prefixPluginTranslations 或手动为键添加你的插件 ID 作为前缀以避免冲突。
  • 提供默认消息。 如果翻译缺失,使用 formatMessage 作为回退时,请始终包含 defaultMessage
  • 优雅地处理缺失的翻译。 registerTrads 函数对于缺失的语言环境应返回空对象,而不是抛出错误。
  • 使用描述性键名。 选择清晰、有层次的键名(例如,settings.general.title 而不是 title1)。
  • 至少支持英语。 提供英文翻译可以确保你的插件开箱即可使用。
  • 验证多种语言环境下的行为。 测试当在管理面板中选择不同的语言环境时,你的插件是否能正常工作。
Note

en 语言环境在 Strapi 中始终可用,并作为备用语言环境。如果所选语言环境缺少翻译,Strapi 会使用英文翻译。

🌐 The en locale is always available in Strapi and serves as the fallback locale. If a translation is missing for a selected locale, Strapi uses the English translation.

Tip

要查看你的 Strapi 实例中可用的语言环境,请检查 src/admin/app.tssrc/admin/app.js 文件中的 config.locales 数组。要在运行时以编程方式访问,请参阅 访问 Redux 存储(注意内部存储结构可能会随着版本变化而改变)。

🌐 To see which locales are available in your Strapi instance, check the config.locales array in your src/admin/app.ts or src/admin/app.js file. For programmatic access at runtime, see Accessing the Redux store (note that internal store structure may change between versions).