Git Product home page Git Product logo

strapi-plugin-translate's Introduction

Strapi-Translate

Strapi v4 - Translate plugin

Manage and automate the translation of content fields.

GitHub package.json version Monthly download on NPM Build Commitizen friendly

plugin showcase

πŸ“­ Requirements

This plugin requires the following, in order to work correctly:

  • Strapi v4 (this plugin is not compatible with v3)
    • Plugin tested for v4.6 to v4.9
  • 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

module.exports = {
  // ...
  translate: {
    enabled: true,
    config: {
      // Add the name of your provider here (for example 'deepl' for strapi-provider-translate-deepl or the full package name)
      provider: '[name]',
      providerOptions: {
        // Your provider might define some custom options like an apiKey
      },
      // Which field types are translated (default string, text, richtext, components and dynamiczones)
      // Either string or object with type and format
      // Possible formats: plain, markdown, html (default plain)
      translatedFieldTypes: [
        'string',
        { type: 'text', format: 'plain' },
        { type: 'richtext', format: 'markdown' },
        'component',
        'dynamiczone',
      ],
      // If relations should be translated (default true)
      translateRelations: true,
    },
  },
  // ...
}

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 provider
  • copy: The original value of the source localization is copied
  • delete: The field is let empty after translation

Relations are again little bit different. The translate option works as described below, the copy option only works when the related content type is not localized and is one way or if bothWays is either manyToOne or manyToMany

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:

{
  //...
  "attributes": {
    //...
    "customField": {
      "type": "customField",
      "pluginOptions": {
        "translate": {
          "translate": "copy"
        },
        "i18n": {
          "localized": true
        }
      }
    }
    //...
  }
  //...
}

πŸš€ 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 the Translate section on the right sidebar
  • Select the desired source to translate from
  • Press the confirmation button

Translate all entities of a content type

Batch translation showcase

  • 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 and resume. 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

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:

Permissions for Translate plugin

πŸ§‘β€πŸ’» 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 example google)
  • a main file declared in the package.json, that exports a provider object:
module.exports = {
  provider: 'google',
  name: 'Google',
  /**
   * @param {object} providerOptions all config values in the providerOptions property
   * @param {object} pluginOptions all config values from the plugin
   */
  init(providerOptions = {}, pluginConfig = {}) {
    // Do some setup here

    return {
      /**
       * @param {{
       *  text:string|string[],
       *  sourceLocale: string,
       *  targetLocale: string,
       *  priority: number,
       *  format?: 'plain'|'markdown'|'html'
       * }} options all translate options
       * @returns {string[]} the input text(s) translated
       */
      async translate(options) {
        // Implement translation
      },
      /**
       * @returns {{count: number, limit: number}} count for the number of characters used, limit for how many can be used in the current period
       */
      async usage() {
        // Implement usage
      },
    }
  },
}

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:

const { chunks, reduceFunction } = strapi
  .service('plugin::translate.chunks')
  .split(textArray, {
    // max length of arrays
    maxLength: 100,
    // maximum byte size the request should have, if a single text is larger it is split on new lines
    maxByteSize: 1024 * 1000 * 1000,
  })
// The reduceFunction combines the split text array and possibly split texts back together in the right order
return reduceFunction(
  await Promise.all(
    chunks.map(async (texts) => {
      // Execute the translation here
      return providerClient.translateTexts(texts)
    })
  )
)

The translate function receives the format of the text as plain, markdown or html. 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:

const { markdownToHtml, htmlToMarkdown } = strapi.service(
  'plugin::translate.format'
)

if (format === 'markdown') {
  return htmlToMarkdown(providerClient.translateTexts(markdownToHtml(text)))
}
return 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)

strapi-plugin-translate's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

strapi-plugin-translate's Issues

DeepL 413 Error

With large translations, we are receiving a 413 error from DeepL, meaning the request size did exceed the allowed 128KiB
https://www.deepl.com/de/docs-api/accessing-the-api/limits/

Is there a way to split up translations into smaller requests? Maybe we can add a translate button to every single field, or have a selection dialog which fields should be translated?

[BUG]: Warning: Failed to translate locale

Describe the bug
In a collection type when I want to Translate from another locale I get the following error:

TypeError: Cannot read properties of undefined (reading 'post')
    at mn (main.500a0f16.js:1258:1010)
    at onClick (main.500a0f16.js:3613:251)
    at Object.Y (main.500a0f16.js:101:9347)
    at Je (main.500a0f16.js:101:9507)
    at He (main.500a0f16.js:101:9570)
    at Fi (main.500a0f16.js:101:29084)
    at Ur (main.500a0f16.js:101:29545)
    at main.500a0f16.js:101:34949
    at Gt (main.500a0f16.js:105:19082)
    at sn (main.500a0f16.js:101:8502)

Attempting the same from the DeepL settings menu works without any issues.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

System (please complete the following information):

  • OS: Mac
  • Browser brave
  • Dependencies:
  "dependencies": {
    "@ckeditor/strapi-plugin-ckeditor": "^0.0.7",
    "@strapi/plugin-documentation": "^4.7.0",
    "@strapi/plugin-i18n": "^4.7.0",
    "@strapi/plugin-open-ai": "^1.0.1",
    "@strapi/plugin-users-permissions": "4.7.0",
    "@strapi/provider-upload-aws-s3": "^4.7.0",
    "@strapi/strapi": "4.7.0",
    "date-fns": "^2.29.3",
    "framer-motion": "^10.0.1",
    "pg": "^8.9.0",
    "pg-connection-string": "^2.5.0",
    "react-select": "^5.7.0",
    "sharp": "^0.31.3",
    "strapi-plugin-config-sync": "^1.1.1",
    "strapi-plugin-deepl": "^0.4.2",
    "strapi-plugin-populate-deep": "^2.0.0",
    "strapi-plugin-sitemap": "^2.0.9"
  },

Plugin config
Please add your plugin config here, e.g.

module.exports = ({ env }) => ({
  // ...
  'config-sync': {
    enabled: true,
    config: {
      syncDir: 'config/sync/',
      minify: false,
      importOnBootstrap: false,
      customTypes: [],
      excludedTypes: [],
      excludedConfig: ['core-store.plugin_users-permissions_grant'],
    },
  },
  deepl: {
    enabled: true,
  },
  upload: {
    config: {
      provider: 'aws-s3',
      providerOptions: {
        accessKeyId: env('AWS_ACCESS_KEY_ID'),
        secretAccessKey: env('AWS_ACCESS_SECRET'),
        region: env('AWS_REGION'),
        params: {
          Bucket: env('AWS_BUCKET'),
        },
      },
    },
  },
  'open-ai': {
    enabled: true,
    config: {
      API_TOKEN: env('OPENAI_API_KEY'),
    },
  },
  'strapi-plugin-populate-deep': {
    config: {
      defaultDepth: 3, // Default is 5
    },
  },
})

Additional context
I tried deleting the whole posts collection, but it seems that somehow the issue persists. How could I further debug this to understand where exactly the translation is failing?

Thanks a ton for your help and developing this amazing plugin in the first place πŸ’ͺ

[BUG]: Issue with empty relations

Describe the bug
Entries with empty Relations cannot be saved after translation

To Reproduce
Steps to reproduce the behavior:

  1. Start playground (on main branch)
  2. Create new article without author/category
  3. Save, switch to other Locale, and translate
  4. Both author and category have on empty entry, preventing the article from being saved

Expected behavior
No category/author should be in the translated article

Screenshots

System (please complete the following information):

  • OS: Linux
  • Browser: Chrome
  • Versions: as in playground on main, i.e. strapi 4.6.0

Plugin config
default config of playground

Additional context
parseRelations replaces

    "author": null,
    "category": null

with

{
    "category": [
        {
            "href": "/content-manager/collectionType/api::category.category/",
            "publicationState": false
        }
    ],
    "author": [
        {
            "href": "/content-manager/collectionType/api::writer.writer/",
            "publicationState": false
        }
    ],
}

but I think it should be an empty Array instead?

Strapi 4.5 support

When using this plugin in combination with strapi 4.5.x i cant use the translate form different locale feature in the content manager.
I traced the problem to the new way of (lazy) loading relations. This means that the data in the admin ui doesn't standard have the id's of the relations resulting in an error when trying to translate:

error: Translating entity failed: Undefined binding(s) detected when compiling WHERE. Undefined column(s): [t0.id] query: where `t0`.`id` = ?

Are you going to offer strapi 4.5 support?

Use deepl-node instead of custom requests

Currently, all requests to the DeepL-API are handwritten. However there exists a (relatively) new package deepl-node that wraps the DeepL-API directly, also including all additional routes that were not yet implemented here.

The process of switching should be quite easy.

batch translation (needs to also 'translate' uids)

Feature Description

Translate all or a specific list of entities of a content type to another locale.

Requirements:

  • Translation should happen in the background
  • Translation can be cancelled
  • Current status can be requested
  • Translation is paused on server shutdown and continued on start
  • A failure in translation should stop the translation and the reason is saved
  • Translation should not send too many requests to the DeepL-API
  • User can configure if translated entities should be published directly or saved as draft if draftPublish feature is enabled

Overview of api usage in admin panel

It is very useful to see the current usage directly in the admin panel, in order to cancel/pause any tasks before the limit is reached

  • Api to retrieve usage /deepl/usage
  • Frontend view of usage

How i can set formality?

Hi,

Π¨ need to set the "formality: prefer_less" so that the language is less formative.
Π ow can i do this using this plugin in strapi?

[BUG]: Knex: Timeout acquiring a connection

Describe the bug
After starting translation with LibreTranslate plugin I'm continiously getting error:

This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
KnexTimeoutError: Knex: Timeout acquiring a connection. The pool is probably full. Are you missing a .transacting(trx) call?
    at Client_PG.acquireConnection (/srv/app/node_modules/knex/lib/client.js:312:26)
    at runNextTicks (node:internal/process/task_queues:61:5)
    at listOnTimeout (node:internal/timers:528:9)
    at processTimers (node:internal/timers:502:7)
    at async Runner.ensureConnection (/srv/app/node_modules/knex/lib/execution/runner.js:287:28)
    at async Runner.run (/srv/app/node_modules/knex/lib/execution/runner.js:30:19)
    at async Object.execute (/srv/app/node_modules/@strapi/database/lib/query/query-builder.js:481:22)
    at async Object.count (/srv/app/node_modules/@strapi/database/lib/entity-manager/index.js:199:19)

To Reproduce
Steps to reproduce the behavior:

  1. Go to '/admin/plugins/translate'
  2. Click on 'Translate' in the row 'Company' and column 'ru'

Expected behavior
Translation should be done of all set of untranslated records without errors.

System:

Plugin.js config

  // ...
  translate: {
    enabled: true,
    config: {
      // Choose one of the available providers
      provider: 'libretranslate',
      // Pass credentials and other options to the provider
      providerOptions: {
        // your API key - required and wil cause errors if not provided
        apiKey: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
        // api url - required 
        apiUrl: 'https://translate.somesite.com/',
        // maximum number of requests per minute - optional, default is `undefined` => no limit
        apiMaxRPM: 40,
        // maximum number of chars per request - optional, default is `undefined` => no limit
        apiMaxChars: 1234,
        // maximum number of texts per request
        apiMaxTexts: 55,
        // manually overwrite the Strapi Locale to LibreTranslate Locale mapping.
        // default is the string before the `-` character for every locale
        localeMap:{ }
      },
      // other options ...
    },
  },
  // ...

Database.js config:

module.exports = ({ env }) => ({
  connection: {
    client: 'postgres',
    connection: {
      host: env('DATABASE_HOST', 'IP'),
      port: env.int('DATABASE_PORT', 5432),
      database: env('DATABASE_NAME', 'database'),
      user: env('DATABASE_USERNAME', 'postgres'),
      password: env('DATABASE_PASSWORD', 'SomePassword'),
//      ssl: env.bool('DATABASE_SSL', false),
      ssl: {
        rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), // For self-signed certificates
      },
    },
    // https://github.com/strapi/strapi/issues/11860
    debug: false,
    acquireConnectionTimeout: 600000,
    pool: {
      min: 0,
      max: 40,
      acquireTimeoutMillis: 300000,
      createTimeoutMillis: 300000,
      destroyTimeoutMillis: 50000,
      idleTimeoutMillis: 300000,
      reapIntervalMillis: 10000,
      createRetryIntervalMillis: 2000,
      propagateCreateError: false,
    },  
  },
});

[BUG]: showing elements in richText

Describe the bug
After translate header splitΡ‹ into 3 lines

Expected behavior
if the title is in one line: then translate it into one line

Screenshots

image

image

System (please complete the following information):

  • OS: windows, macOS
  • Browser chrome
  • "@strapi/strapi": "4.7.0",
  • "strapi-plugin-translate": "1.1.0",
  • "strapi-provider-translate-deepl": "1.1.0"
    Plugin config
    module.exports = {
    translate: {
    enabled: true,
    config: {
    apiKey: process.env('DEEPL_API_KEY'),
    provider: 'deepl',
    providerOptions: {
    apiKey: process.env('DEEPL_API_KEY'),
    apiOptions: {
    formality: 'prefer_less',
    }
    },
    translateRelations: true,
    glossaryId: 'customGlossary',
    },
    },
    };

[BUG]: When we use translator we have a problem with bound entities

Describe the bug
When translating a page from English into German, an English category is attached to the German page

To Reproduce
Steps to reproduce the behavior:

  1. Go to Germany page (already having a page with an associated category in English)
  2. Click on translate from another locale & select English
  3. And after we can see that for our Germany page connected Englissh category

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
https://www.screencast.com/t/aRtpKpG67op

System (please complete the following information):

  • OS: MacOs, Windows
  • Browser firefox, safari, chrome
  • Versions:
    • strapi: "4.7.1"
    • strapi-plugin-translate:"1.1.1"
    • strapi-provider-translate-deepl@: "1.1.1"

Plugin config
module.exports = {
translate: {
enabled: true,
config: {
apiKey: process.env('DEEPL_API_KEY'),
provider: 'deepl',
providerOptions: {
apiKey: process.env('DEEPL_API_KEY'),
apiOptions: {
formality: 'prefer_less',
}
},
translateRelations: true,
glossaryId: 'customGlossary',
},
},
};

Translating entity failed with source language 'en'

When translating an entity from locale en to any other the following error is displayed:

error: Translating entity failed: Bad request, message: Value for 'source_lang' not supported

This is because EN-GB or EN-US is only supported for target_lang but not for source_lang

[BUG]: No correct direct translation with relations in components

Describe the bug
We have some relations in components.
Add. after use translator we cannot use with it.

To Reproduce
Steps to reproduce the behavior:

  1. Go to my entity not default language
  2. Click on 'translate from another locale'. selected language
  3. I cannon save entity because relation in my component empty
  4. I go to component click on one component. And all my components set as open. I want to select category and I click on it field
  5. I got white screen and all my work can not be save

Expected behavior
I can click on select category. Selector opened and I set category

Screenshots
I recorded screencast for you https://www.screencast.com/t/RFtPDmgD85v

System (please complete the following information):

  • OS: macOS, windows
  • Browser chrome
  • Versions:
    • "@strapi/strapi": "4.7.0",
    • "strapi-plugin-translate": "1.1.0",
    • "strapi-provider-translate-deepl": "1.1.0"

Plugin config
module.exports = {
translate: {
enabled: true,
config: {
apiKey: env('DEEPL_API_KEY'),
provider: 'deepl',
providerOptions: {
apiKey: env('DEEPL_API_KEY'),
apiOptions: {
formality: 'prefer_less',
}
},
translateRelations: true,
glossaryId: 'customGlossary',
},
},
};

Additional context
Add any other context about the problem here.
Sometimes after translate fields with relations we have wrong connection with category.
for example: for Germany lang after translate can be set English category. It is wrong.
it should not be possible to establish a relationship with different languages ​​for entities

End to end tests

Is your feature request related to a problem? Please describe.
Since the integration with the strapi admin api is very deep, end to end testing would provide a sense of stability about the implementation.

Describe the solution you'd like
The process of e2e tests is split into two parts:

  1. Testing of the server code, apis and integration with the strapi database.
  2. Testing of the admin interface code and the integration with strapi admin apis.

Although the second is very dependent on the first, the two parts still need to be tested separately, because the culprit can be easier identified. Also, the server code needs deeper integration to be able to be tested, whereas the admin interface has the browser as interaction.

A wonderful addition would be to also have the tests run on the different supported strapi versions, however that might complicate the process a lot.

Additional context
I though there was a very good example on how to do this, however I could only find the following plugin that does some integration tests:

Saving entity after translating not possible in strapi 4.5.x

After translating an entity directly from another locale, it is not possible to save the entity.

The following call stack is displayed in the console when in watch-admin mode:

TypeError
error.response is undefined
Call Stack
 getAPIInnerErrors
  lib/src/utils/getAPIInnerErrors/index.js:6:24
 EditViewDataManagerProvider/handleSubmit
  webpack-internal:///69413:377:133
 fulfilled
  webpack-internal:///69413:56:24
 promise callback*step
  webpack-internal:///69413:68:76
 __async/<
  webpack-internal:///69413:69:9
 __async
  webpack-internal:///69413:53:10
 EditViewDataManagerProvider/handleSubmit<
  webpack-internal:///69413:359:19
 callCallback
  webpack-internal:///63464:3946:14
 invokeGuardedCallbackDev
  webpack-internal:///63464:3995:16
 invokeGuardedCallback
  webpack-internal:///63464:4057:31

Source of the bug currently unknown. Affected strapi versions at least 4.5.6 possibly earlier ones as well

[dev] error: Could not load translate provider "deepl"

Hello, I configured this plugin and here is my plugin part of the code :

translate: {
    enabled: true,
    config: {
      provider: "deepl",
      providerOptions: {
        apiKey: MY_API_KEY,
        apiUrl: "https://api-free.deepl.com",
        localeMap: {
          EN: "EN-US",
        },
        apiOptions: {
          formality: "default",
        },
      },
    },
  },

I did run build & develop again, but I'm still getting this issue when trying develop : error: Could not load translate provider "deepl".

I did already have a similar error in the past, and the plugin wasn't well setup. However here I'm not sure which steps I'm missing. Any suggestions or help ?

thank you for your help.

TypeError: Cannot read properties of undefined (reading 'post')

Describe the bug
After clicking 'Translate from another locale' in the editor mode of a post on Strapi, I get the 'Warning: Failed to translate locale' message. Authentication with the DeepL API seems to be working.

To Reproduce
Steps to reproduce the behavior:

  1. Go to the Content Manager
  2. Create a new post, article or another object of a collection, write content in one language
  3. Switch to another locale, click 'Translate from another locale', choose the locale the original content was written in.
  4. Click 'Yes, fill in' and see the rror

Expected behavior
I would have expected the translated text to show up in the empty text field of the secondary locale.

System (please complete the following information):

  • OS: Debian Linux 11 bullseye
  • Browser: chrome
  • Versions:
    • strapi: 4.8.2
    • strapi-plugin-deepl: 0.4.2

Plugin config

module.exports = {
  deepl: {
      enabled: false,
      config: {
            apiKey: 'my-api-key',
            freeApi: true,
            translatedFieldTypes: [
            'string',
            'text',
            'richtext',
            'component',
            'dynamiczone',
          ],
            translateRelations: true,
            glossaryId: 'customGlossary',
          },
    },
}

Full error message

TypeError: Cannot read properties of undefined (reading 'post')
    at wn (main.1680c8bb.js:761:1007)
    at onClick (main.1680c8bb.js:3069:251)
    at Object.V (main.1680c8bb.js:807:9306)
    at dt (main.1680c8bb.js:807:9467)
    at Ke (main.1680c8bb.js:807:9529)
    at Lr (main.1680c8bb.js:807:28928)
    at Di (main.1680c8bb.js:807:29380)
    at main.1680c8bb.js:807:34770
    at Zt (main.1680c8bb.js:811:18950)
    at un (main.1680c8bb.js:807:8469)

Additional context
I am running my Strapi app in a Docker container.

Update translations

Hello! This plugin batch translation works fine only when you have created new item.
I need batch translation of updated items.
It look's like updated items should be marked somehow to be avaliable for batch update of previously batch translated languages.

Roadmap

  • Ignore fields that are not translated
  • Ignore dates, enumeration, email, json
  • Allow translation of nested data
    • components
    • dynamic zones
  • #23
  • #24
  • #6
  • translate relations as well
  • #25
  • #13
  • Tests
    • unit
    • e2e

error: Translating entity failed: Request failed with status code 400

I am trying to set up deepl plugin for automated translation. followed the doc - https://market.strapi.io/plugins/strapi-plugin-deepl#configuration for the set up however there is no luck. I even tried to register with paid version but the moment i do translate from other locale under content-management I get a warning : Bad request to Deepl Api and in logs - translating entity failed: Request failed with status code 400 . It seems a configuration issue from the Strapi end.. could you please advise?

Below is the error printed in logs.

[2023-01-06 14:31:12.401] error: Translating entity failed: Request failed with status code 400
[2023-01-06 14:31:12.402] http: POST /deepl/translate (223 ms) 400

Configuration of plugin options also through admin

The current way to configure the API Token and if the free or paid API is used can only be configured in the config file or through environment variables.
Since there is no other way to set these, the application setup is currently cancelled if these are not set.

It would be very useful, to allow configuration through the admin panel, since then also not so techy people can set it up.

Inspiration: https://github.com/meilisearch/strapi-plugin-meilisearch#-add-credentials-

  • Allow api token and free api configurations to be empty
  • Configure a router, controller and service to set and retrieve settings from the plugin store
    • Do not allow changing the settings if the values were set through the plugin configuration / environment variables
    • Use values from plugin configuration if they are set
  • Create configuration page to set/update these values
    • Grey out fields if values from plugin configuration are present
  • Show meaningful error messages when the credentials are not set up yet

Content not being translated

Steps to reproduce:

  1. Open a document with English locale filled in
  2. Switch to French
  3. Press "Translate from another locale"
  4. Select English

Expected result:

Fields get translated

Actual result:

Fields get filled with English as if the native "Fill in from another locale" has been pressed.
The admin dashboard sends a POST request to /translate/translate with the following payload:

{
  "id":1,
  "sourceLocale":"en",
  "targetLocale":"fr",
  "contentTypeUid":"api::about.about"
}

and gets this 200 response:

{
  "name": "The name of the document",
  "createdAt": "2023-01-04T08:49:43.842Z",
  "updatedAt": "2023-01-20T17:41:35.467Z",
  "locale": "en",
  "body": "Some text in english, not translated",
  "another_field": "Not translated also"
  "createdBy": {
    "id": 1
  },
  "updatedBy": {
    "id": 1
  },
  "localizations": [
    {
      "id": 2
    },
    {
      "id": 1
    }
  ]
}

Plugin config:

// plugins.ts
export default ({ env }) => ({
  translate: {
    enabled: true,
    config: {
      provider: 'deepl',
      providerOptions: {
        apiKey: 'xxx'
      },
    },
  },
})

Versions:

"@strapi/strapi": "4.5.5",
"strapi-plugin-translate": "1.0.0",
"strapi-provider-translate-deepl": "1.0.0",

Compatibility with CKEditor 5 custom field ?

Hi guys,

I was using Strapi Plugin CKEditor until I found out it was not maintained that much and above all that an official CKEditor plugin was available.

I installed it and replaced my richtext fields by customField eg:


Before:

{
    "excerpt": {
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      },
      "type": "richtext",
      "required": true
    },
}

After:

{    
"excerpt": {
      "pluginOptions": {
        "i18n": {
          "localized": true
        },
        "deepl": {
          "translate": "translate"
        }
      },
      "type": "customField",
      "options": {
        "preset": "rich"
      },
      "customField": "plugin::ckeditor.CKEditor",
      "required": true
    }

}

But when I try to translate from another locale, I have this error :
image

Any idea why ?

Thanks !

Usage with Docker: apiKey not set

Hi dev team,

thanks for this very nice plugin!

I'm currently trying to build a stateless strapi production version on AWS Fargate using Docker.

So far everything works quite fine, but whenever I'm trying to include the DeepL Plugin, I get the following error:

error: Error regarding deepl config: apiKey is not set, disable the plugin if you do not have one since the translations will not succeed

I tried setting the environment variable on as a docker run parameter as well as in my AWS Fargate config. Somehow it's not consumed.

Any ideas?

Dockerfile:

ARG NODE_VERSION=14
FROM node:${NODE_VERSION}-alpine AS base-alpine
EXPOSE 1337

FROM base-alpine

ARG STRAPI_VERSION=latest

RUN yarn global add @strapi/strapi@${STRAPI_VERSION}


RUN mkdir -p /srv/app && chown 1000:1000 -R /srv/app

WORKDIR /srv/app

VOLUME /srv/app

COPY docker-entrypoint.sh /usr/local/bin/

RUN chmod 777 /usr/local/bin/docker-entrypoint.sh && ln -s /usr/local/bin/docker-entrypoint.sh

ENTRYPOINT ["docker-entrypoint.sh"]

CMD ["strapi"]

docker-entrypoint.sh:

#!/bin/sh
set -ea

if [ "$*" = "strapi" ]; then

  if [ ! -f "package.json" ]; then

    DATABASE_CLIENT=${DATABASE_CLIENT:-sqlite}

    EXTRA_ARGS=${EXTRA_ARGS}

    echo "Using strapi $(strapi version)"
    echo "No project found at /srv/app. Creating a new strapi project ..."

    DOCKER=true strapi new . --no-run \
      --dbclient=$DATABASE_CLIENT \
      --dbhost=$DATABASE_HOST \
      --dbport=$DATABASE_PORT \
      --dbname=$DATABASE_NAME \
      --dbusername=$DATABASE_USERNAME \
      --dbpassword=$DATABASE_PASSWORD \
      --dbssl=$DATABASE_SSL \
      $EXTRA_ARGS

  elif [ ! -d "node_modules" ] || [ ! "$(ls -qAL node_modules 2>/dev/null)" ]; then

    if [ -f "yarn.lock" ]; then

      echo "Node modules not installed. Installing using yarn ..."
      yarn install --prod --silent

    else

      echo "Node modules not installed. Installing using npm ..."
      npm install --only=prod --silent

    fi

  fi

  if [ "$NODE_ENV" = "production" ]; then
    STRAPI_MODE="start"
  elif [ "$NODE_ENV" = "development" ]; then
    STRAPI_MODE="develop"
  fi


  echo "Installing plugins..."
  yarn add @strapi/plugin-graphql @strapi/plugin-seo strapi-plugin-deepl

  echo "Rebuilding app to make env changes appear..."
  strapi build

  echo "Starting your app (with ${STRAPI_MODE:-develop})..."
  exec strapi "${STRAPI_MODE:-develop}"

else
  exec "$@"
fi

Advanced Markdown and HTML translation

Rationale

Markdown is not officially supported by DeepL, so there are sometimes issues with the translation. In particular the following issues have been discovered:

  • non-absolute link targets are sometimes translated
  • Text formatting sometimes removed (** at the end of lines)

Also some people use not Markdown in the richtext fields, but HTML (for example with the HTML editor or CK Editor), so this should not be translated but directly interpreted as HTML

Approach

DeepL supports xml tag filtering and beta HTML support, so HTML should work well and Markdown could be parsed to HTML and back.

  • Configuration of the behaviour
    For each field type allow adding settings either with string or with object of shape {type: string, format: 'plain' | 'markdown' | 'html'}
  • parse markdown to html and back (watching out for custom syntax!)
  • send html in separate requests with deepl api and with option tag_handling=html

Translated document won't save and throws error

If a document has a hasOne relation that is set to translate, then it will not be translated. Said document will not be able to save and will throw the following error in the console:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'data')
    at qc (main.96060888.js:13200:3220)
    at content-manager.9fb36cc0.chunk.js:9856:140
    at Generator.next (<anonymous>)
    at fulfilled (content-manager.9fb36cc0.chunk.js:9534:24)

Here's a reproduction repo: https://github.com/anatolykopyl/plugin-translate-reproduction

repro.mp4

Incorrect link formatting in markdown translations

I noticed that after translating markdown text, the links generated are not in the expected format. The links appear as follows:

[Text link](<https://example.com>)

Instead of the expected format:

[Text link](https://example.com)

It's getting worse when your link ends with a ):

[Text link](https://example.com/?title=(title))

Becomes:

[Text link](<https://example.com/?title=(title>))

Please let me know if additional information is needed to resolve this issue. Thank you!

I could not find an issue for that, sorry if it's redundant or already discussed.

JS error translations not working (Strapi >= 4.1.4)

I've been trying to get this plugin working, installing going well. The plugin is there and enabled, the fields appear in the editor. The copy function from other language does work but translation feature from a source language isn't.

When I trigger a translation i get an console error:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading β€˜data’) at main.50a6bd34.js:2:1487884 at c (main.50a6bd34.js:2:2132705) at Generator._invoke (main.50a6bd34.js:2:2132493) at Generator.next (main.50a6bd34.js:2:2133134) at t (main.50a6bd34.js:2:562) at u (main.50a6bd34.js:2:773)

Screenshot 2022-03-18 at 14 38 18

I did run npm audit as well which returns:

ansi-regex >2.1.1 <5.0.1 Severity: moderate Inefficient Regular Expression Complexity in chalk/ansi-regex - https://github.com/advisories/GHSA-93q8-gq69-wqmw fix available via npm audit fix --force Will install [email protected], which is a breaking change node_modules/plop/node_modules/ora/node_modules/ansi-regex strip-ansi 4.0.0 - 5.2.0 Depends on vulnerable versions of ansi-regex node_modules/plop/node_modules/ora/node_modules/strip-ansi ora 2.0.0 - 4.0.2 Depends on vulnerable versions of strip-ansi node_modules/plop/node_modules/ora plop 2.2.0 - 2.7.6 Depends on vulnerable versions of ora node_modules/plop @strapi/generators * Depends on vulnerable versions of plop node_modules/@strapi/generators @strapi/plugin-content-type-builder * Depends on vulnerable versions of @strapi/generators node_modules/@strapi/plugin-content-type-builder @strapi/strapi * Depends on vulnerable versions of @strapi/generators node_modules/@strapi/strapi strapi-plugin-deepl >=0.1.3 Depends on vulnerable versions of @strapi/strapi node_modules/strapi-plugin-deepl

Any ideas here on how to get it working?

Allow more customizations when translating

Currently, the default translation behavior of DeepL is used. However, it supports many additional options that should be provided to the user when starting a translation (directly or batch).

The relevant options are:

  • split_sentences
  • preserve_formatting
  • formality: default, prefer_more or prefer_less
  • glossary_id: glossary to use for this translation

Following things need to be done:

  • Allow setting defaults for these values through the configuration (or the admin panel, see #23)
  • Configure router and service to handle these options
  • Configure translation modals to allow setting these options

[BUG]: BatchTranslateJob doesn't work correctly when entityIds are provided

Describe the bug
The BatchTranslateJob supports providing entityIds which is supposed to limit the untranslated entities that it translates. This doesn't currently work and is essentially a no-op, in that the job succeeds but nothing is translated.

Currently

To Reproduce
Steps to reproduce the behavior:

  1. Choose a content type and an ID from the source locales that is currently missing a translation in a given target locale
  2. Via cURL or similar, send a request using a valid auth token to run the batch translation job using the above contentType, sourceLocale, targetLocale and entityIds as an array containing the selected ID
curl 'http://localhost:1337/translate/batch-translate' \
  -H 'Authorization: <AUTH_TOKEN>' \
  -H 'Content-Type: application/json' \
  --data-raw '{"contentType":"<CONTENT_TYPE>","sourceLocale":"<SOURCE_LOCALE>","targetLocale":"<TARGET_LOCALE>","autoPublish":true,"entityIds":[<ENTITY_ID>]}' \
  --compressed
  1. Observe that the response is successful (e.g. for my content type cms-biotype with an en-US entity ID 27 translating into es-419 I received the following response:
{"data":{"id":51,"contentType":"api::cms-biotype.cms-biotype","sourceLocale":"en-US","targetLocale":"es-419","entityIds":[27],"status":"created","failureReason":null,"progress":0,"autoPublish":true,"createdAt":"2023-05-04T09:26:08.661Z","updatedAt":"2023-05-04T09:26:08.661Z"}}% 
  1. Query the batch jobs table and observe that the job did indeed run and has a successful finished status and the entityIds column is populated with the requested entity IDs

Screenshot 2023-05-04 at 10 30 21

  1. Go to the source locale listings page for the given content type and find the requested entity ID
  2. In localizations, observe that the target locale is still untranslated/doesn't exist

Expected behavior
For the provided entityIds the job should run successfully and the untranslated entityIds should be translated

Screenshots
(See above)

System (please complete the following information):

  • OS: N/A
  • Browser: N/A
  • Versions:
    • strapi: v4.9.2
    • strapi-plugin-translate: v1.1.3
    • provider: custom provider (strapi-provider-translate-thrive-transifex)
  • Other Strapi plugins:

Plugin config
Please add your plugin config here, e.g.

module.exports = {
translate: {
    enabled: true,
    config: {
      provider: 'strapi-provider-translate-thrive-transifex',
      providerOptions: {},
      translatedFieldTypes: [
        'string',
        { type: 'text', format: 'plain' },
        { type: 'richtext', format: 'plain' },
        'component',
        'dynamiczone',
      ],
      translateRelations: true
    }
  }
}

Additional context
Add any other context about the problem here.

[FEATURE]: Translate slug field

I have two fields:

  • Title
  • Slug

When using the plugin it successfully translate the title but the "slug" is left with default value (entity-{id}).
How can I set the slug field as slugify(title) ? Is there a way I could override this by myself?

[BUG]: Could not load translate provider "deepl".

I have installed the provider plugin but I got this error when I start server

npm run build && npm run develop

Could not load translate provider "deepl".

package.json

"dependencies": {
    "@retikolo/drag-drop-content-types": "^1.3.4",
    "@strapi/plugin-i18n": "4.8.1",
    "@strapi/plugin-users-permissions": "4.8.1",
    "@strapi/provider-upload-cloudinary": "^4.8.1",
    "@strapi/strapi": "4.8.1",
    "better-sqlite3": "8.0.1",
    "pg": "^8.10.0",
    "pg-connection-string": "^2.5.0",
    "strapi-plugin-deepl": "^0.4.2",
    "strapi-plugin-translate": "^1.1.2"
  },

config/plugins.js

module.exports = ({ env }) => ({
    
  translate: {
    enabled: true,
      config: {
        // Choose one of the available providers
        provider: 'deepl',
        providerOptions: {

          apiKey: env('DEEPL_API_KEY'),

          apiUrl: 'https://api-free.deepl.com',
          localeMap: {
            EN: 'EN-US',
          },
          apiOptions: {
            formality: 'prefer_less',
            glossary: 'XXXXXX-19c3-42a8-b456-ffc995bac508'
          }
        },

        translatedFieldTypes: [
          'string',
          { type: 'text', format: 'plain' },
          { type: 'richtext', format: 'markdown' },
          'component',
          'dynamiczone',
        ],
    
      },
  },

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

System (please complete the following information):

  • OS: [e.g. Linux]
  • Browser [e.g. firefox, safari, chrome]
  • Versions:
    • strapi: [e.g. 4.6.0]
    • strapi-plugin-translate: [e.g. 1.1.0]
    • provider: [e.g. [email protected]]
  • Other Strapi plugins:

Plugin config
Please add your plugin config here, e.g.

module.exports = {
  translate: {
    enabled: true,
    config: {
      provider: '[e.g. deepl]',
      providerOptions: { },
      translateRelations: true,
    },
  },
}

Additional context
Add any other context about the problem here.

[FEATURE]: Development: automatically check files that should be identical

Is your feature request related to a problem? Please describe.
Some files are exact or mostly copies of each other (i.e. the main README and the plugin README, or the package.json in the playground and its template). When editing any of those, one must remember to edit both files in the same way. Sometimes changes are made to only one of them.

Describe the solution you'd like
Either a pre-commit hook or a Github Action checking those files for differences.

Describe alternatives you've considered
Comparing the files by hand during PR review, i.e. the way it is done right now. But sometimes incomplete changes still go undetected, and quicker feedback could avoid unnecessary delays.

Additional context
Should not only consider exact matches, but also work for the template version of the package.json file in the playground, which should be identical to the package.json file except for the versions.

Error saying targetLang='en' is deprecated, please use 'en-GB' or 'en-US' instead

When translating from a non-English locale to English, I get this error.

targetLang='en' is deprecated, please use 'en-GB' or 'en-US' instead.

image

Versions:

{
  "dependencies": {
    "@strapi/plugin-i18n": "4.5.6",
    "@strapi/plugin-users-permissions": "4.5.6",
    "@strapi/strapi": "4.5.6",
    "better-sqlite3": "7.4.6",
    "pg": "8.6.0",
    "strapi-plugin-translate": "^1.0.0",
    "strapi-provider-translate-deepl": "^1.0.0"
  }
}

Allow specifying for each field to be included in the translation or not

  • Add Configuration to Content-Type-Builder (Content-Type and Components)
    - Options: translate, copy, delete
    - Default: translate for all fields conforming to plugin config
    - Special handling for relations?
  • Filter fields copy and delete in translatable-fields
  • Add new method to remove fields with delete setting
  • Tests

Every field populating: translated: Text

I updated the plugin, but it seems that everytime I try to do a translation it populates all fields with: "translated: Text" that's it. What could be the reason?

[BUG]: Cannot read properties of undefined (reading 'row')

Describe the bug
After updating strapi to version 4.8.2 i get the error Cannot read properties of undefined (reading 'row') when trying to access the plugin on the dashboard: http://localhost:1337/admin/plugins/translate

It seems the template of the page gets loaded. It's visible for a short time, then javascript throws this error and the whole page gets blank.

To Reproduce
Steps to reproduce the behavior:

  1. Update Strapi to latest version
  2. Login to dashboard
  3. Click Translate in the left sidebar
  4. See error in console and page getting blank

Expected behavior
The overview of translatable content-types as intended.

Screenshots
image

System:

Plugin config
Please add your plugin config here, e.g.

module.exports = {
	translate: {
		enabled: true,
		config: {
			provider: 'deepl',
			providerOptions: {
				apiKey: [apiKey],
				freeApi: true,
			},
			translatedFieldTypes: [
				'string',
				'text',
				'richtext',
				'component',
				'dynamiczone',
			],
			translateRelations: true
		}
	},
}

[SUPPORT]: Strapi moves to react 18

Just got this email from strapi:

We’re currently looking at upgrading to react18 in v4 of Strapi, and we would therefore like plugin developers to test their plugins against this version: npx create-strapi-app@react18 react-18. If you have any issues, could you please post them in our dedicated Discord channel (v4-react18)?

we should add the react18 tag to the versions in the e2e tests and verify that it still works

Translation error when translating empty / unset components with relations

I am having the following issue: even though I have specified the following config:
translateRelations: false,
and
translatedFieldTypes: [
'string',
'text',
'richtext'
],

I still get an error for a component related field:
message: "Cannot set properties of null (setting 'szallashely_kategoria')"

Even better would be that I could specify the exact fields (e.g. inside a specific content type) that should be translated.

Using the latest version ( "strapi-plugin-deepl": "^0.3.5")

Thanks!
Bence

Localizations are removed after translating

If you issue the translate function which makes it fill in translated:Text and then select the language dropdown it shows the other languages (including the source) as empty.
Screenshot 2022-03-21 at 11 51 58

Also clicking a language ?(empty) to translate it shows this in console:

Screenshot 2022-03-21 at 11 54 56

Originally posted by @cookiefirst in #3 (comment)

Provider System

DeepL is not the only system that provides automated translation. And this plugin already includes a llot of code that would be duplicated in other translate apps

Maybe it would be a better approach to call the plugin strapi-plugin-translate and add providers like

  • strapi-provider-translate-deepl or
  • strapi-provider-translate-google

This provider system could also be used to support "manual translations" or external translation managers (see for example @localazy/strapi-plugin)

Allow translating documents

This is a possibly big feature with little relevance for most people. However DeepL supports this, so it could be included relatively easy

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.