Translate
Manage and automate the translation of content fields
📭 Requirements
This plugin requires the following, in order to work correctly:
- Strapi v4 (this plugin is not compatible with v3)
- Plugin tested for
v4.19
tov4.25
- Plugin tested for
- The plugin i18n installed and enabled (
@strapi/plugin-i18n
[npm]) - The content type to have internationalization enabled (advanced settings in the content type builder)
- In the internationalization settings at least two locales
- A translation provider that executes the actual translation (see Configuration)
Unless you have the previous set up, the field on the right where you can translate will not show up. Also it will not show up when editing the currently only available translation of an entry.
⏳ Installation
# with npm
$ npm install strapi-plugin-translate
# or with yarn
$ yarn add strapi-plugin-translate
After successful installation you have to build a fresh package that includes plugin UI:
# with npm
$ npm run build && npm run develop
# or with yarn
$ yarn build && yarn develop
⚙ Configuration
Overall plugin configuration
The overall plugin configuration is done through
config[/env]/plugins.js
or environment variables
1module.exports = {
2 // ...
3 translate: {
4 enabled: true,
5 config: {
6 // Add the name of your provider here (for example 'deepl' for strapi-provider-translate-deepl or the full package name)
7 provider: '[name]',
8 providerOptions: {
9 // Your provider might define some custom options like an apiKey
10 },
11 // Which field types are translated (default string, text, richtext, components and dynamiczones)
12 // Either string or object with type and format
13 // Possible formats: plain, markdown, html, jsonb (default plain)
14 translatedFieldTypes: [
15 'string',
16 { type: 'blocks', format: 'jsonb' },
17 { type: 'text', format: 'plain' },
18 { type: 'richtext', format: 'markdown' },
19 'component',
20 'dynamiczone',
21 ],
22 // If relations should be translated (default true)
23 translateRelations: true,
24 // ignore updates for certain content types (default [], i.e. no content types are ignored)
25 ignoreUpdatedContentTypes: ['api::category.category'],
26 // wether to regenerate uids when batch updating (default false)
27 regenerateUids: true
28 },
29 },
30 // ...
31}
Available providers
Configure translation of individual fields/attributes
There are two options to configure translation of individual fields. Both are configured either in the Content-Type Builder in the admin interface in development mode, or in the pluginOptions
property in the schema file.
Disable localization completely
This is part of the i18n
-plugin and available in all field types except relation
, uid
under the name Enable localization for this field
.
Set this value to false, and the field will not be translated. However it will be copied and have the same value for all localizations.
Configure behavior of automated translation
For the field types component
, dynamiczone
, media
, relation
, richtext
, string
, text
, you can additionally configure the behavior when translating automatically under the name Configure automated translation for this field?
. There are three options:
translate
: The field is automatically translated using the providercopy
: The original value of the source localization is copieddelete
: The field is let empty after translation
Relations are again little bit different. The
translate
option works as described below, thecopy
option only works when the related content type is not localized and is one way or if bothWays is eithermanyToOne
ormanyToMany
If you have other fields (e.g. custom fields) for which you want to configure the translation, this cannot be done through the Content-Type Builder, but only in the schema file:
1{
2 //...
3 "attributes": {
4 //...
5 "customField": {
6 "type": "customField",
7 "pluginOptions": {
8 "translate": {
9 "translate": "copy"
10 },
11 "i18n": {
12 "localized": true
13 }
14 }
15 }
16 //...
17 }
18 //...
19}
🚀 Features
This plugin allows you to automatically translate content types. This can be done either on a single entity, or for all entities of a content type.
The following features are included:
- Fill in and translate any locale from another already defined locale
- Translation is restricted by permissions to avoid misuse of api quota
- Configure which field types are translated in the plugin configuration
- Fields that are marked as not localized in the content-type settings will not be translated
- Components and Dynamic zones are translated recursively
- Relations are translated (if enabled in the configuration) if possible
Translate a single entity
- Open the entity that you want to translate
- Select a different (possibly unconfigured) locale in the
Internationalization
section on the right sidebar - Click the link for
Translate from another locale
in theTranslate
section on the right sidebar - Select the desired source to translate from
- Press the confirmation button
Translate all entities of a content type
- Open the Translate plugin section in the left menu
- You now see an overview of all localized content types
- For each language and each content type you have 4 actions:
translate
,cancel
,pause
andresume
. Most actions are disabled, since no job is running. - Press the
translate
button, select the source locale and if already published entries should be published as well (Auto-Publish option) - Start the translation.
Additional remarks:
- If a batch translation is running and the server is stopped, the translation will be resumed on a restart
- If entities are added after the starting the translation, they will not be translated
- UIDs are automatically translated in batch translation mode, since otherwise the entities could not be created/published
- If an error occurs, this will be shown in the logs or the message can be accessed by hovering over the
Job failed
badge
Retranslating updated entities
If a localized entity is updated, an entry is added to the batch update section of the admin page. This allows easy retranslation of updated entities.
By default, uids will be ignored. You can opt to regenerate them by setting the translate.config.regenerateUids
key of the plugin options to true
.
The translate.config.ignoreUpdatedContentTypes
key of the plugin options can be used to define an array of content types for which such updates should not be recorded.
Note that updates are only considered if they trigger the afterUpdate
lifecycle hook provided by strapi.
Schema for translating relations
The related objects are not translated directly, only the relation itself is translated
the related content type is localized
- if a localization of the relation with the targetLocale exists -> it is used
- else the relation is removed
the related content type is not localized
- the relation goes both ways and would be removed from another object or localization if it was used (the case with oneToOne or oneToMany) -> it is removed
- otherwise the relation is kept
🔐 Permissions
Since RBAC was moved to the community edition in Strapi v4.8.0, permissions for endpoints of direct translation, batch translation and api usage can now be granted to other roles than super admins:
🧑💻 Creating your own translation provider
A translation provider should have the following:
- be a npm package that starts with
strapi-provider-translate
and then your provider name (for examplegoogle
) - a main file declared in the package.json, that exports a provider object:
1module.exports = {
2 provider: 'google',
3 name: 'Google',
4 /**
5 * @param {object} providerOptions all config values in the providerOptions property
6 * @param {object} pluginOptions all config values from the plugin
7 */
8 init(providerOptions = {}, pluginConfig = {}) {
9 // Do some setup here
10
11 return {
12 /**
13 * @param {{
14 * text:string|string[],
15 * sourceLocale: string,
16 * targetLocale: string,
17 * priority: number,
18 * format?: 'plain'|'markdown'|'html'
19 * }} options all translate options
20 * @returns {string[]} the input text(s) translated
21 */
22 async translate(options) {
23 // Implement translation
24 },
25 /**
26 * @returns {{count: number, limit: number}} count for the number of characters used, limit for how many can be used in the current period
27 */
28 async usage() {
29 // Implement usage
30 },
31 }
32 },
33}
If your provider has some limits on how many texts or how many bytes can be submitted at once, you can use the chunks
service to split it:
1const { chunks, reduceFunction } = strapi
2 .service('plugin::translate.chunks')
3 .split(textArray, {
4 // max length of arrays
5 maxLength: 100,
6 // maximum byte size the request should have, if a single text is larger it is split on new lines
7 maxByteSize: 1024 * 1000 * 1000,
8 })
9// The reduceFunction combines the split text array and possibly split texts back together in the right order
10return reduceFunction(
11 await Promise.all(
12 chunks.map(async (texts) => {
13 // Execute the translation here
14 return providerClient.translateTexts(texts)
15 })
16 )
17)
The translate function receives the format of the text as plain
, markdown
, html
or jsonb
(Blocks Content Type). If your translation provider supports only html, but no markdown, you can use the format
service to change the format before translating to html
and afterwards back to markdown
. Similarly you can convert jsonb
to html
:
1const { markdownToHtml, htmlToMarkdown, blocksToHtml, htmlToBlocks } = strapi.service(
2 'plugin::translate.format'
3)
4
5if (format === 'markdown') {
6 return htmlToMarkdown(providerClient.translateTexts(markdownToHtml(text)))
7}
8
9if (format === 'jsonb') {
10 return htmlToBlocks(providerClient.translateTexts(blocksToHtml(text)))
11}
12
13return providerClient.translateTexts(texts)
⚠ Limitations:
- The translation of Markdown and HTML may vary between different providers
- Relations that do not have a translation of the desired locale will not be translated. To keep the relation you will need to translate both in succession (Behaviour for multi-relations has not yet been analyzed)
Install now
npm install strapi-plugin-translate
Create your own plugin
Check out the available plugin resources that will help you to develop your plugin or provider and get it listed on the marketplace.