PHPackages                             digitalpulsebe/craft-multi-translator - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Localization &amp; i18n](/categories/localization)
4. /
5. digitalpulsebe/craft-multi-translator

ActiveCraft-plugin[Localization &amp; i18n](/categories/localization)

digitalpulsebe/craft-multi-translator
=====================================

Translate content of elements using the external services

2.28.3(1w ago)47.2k↑41.4%13[6 issues](https://github.com/digitalpulsebe/craft-multi-translator/issues)proprietaryPHPCI passing

Since Oct 12Pushed 1w ago2 watchersCompare

[ Source](https://github.com/digitalpulsebe/craft-multi-translator)[ Packagist](https://packagist.org/packages/digitalpulsebe/craft-multi-translator)[ RSS](/packages/digitalpulsebe-craft-multi-translator/feed)WikiDiscussions develop Synced today

READMEChangelog (10)Dependencies (15)Versions (117)Used By (0)

Multi Translator
================

[](#multi-translator)

Translate your site content using Deepl, Google Translate or ChatGPT.

Requirements
------------

[](#requirements)

This plugin requires Craft CMS 4 or Craft CMS 5.

Installation
------------

[](#installation)

You can install this plugin from the [Plugin Store](https://plugins.craftcms.com/multi-translator) or with Composer.

#### From the Plugin Store

[](#from-the-plugin-store)

Go to the Plugin Store in your project’s Control Panel and search for “Multi Translator”. Then press “Install”.

#### With Composer

[](#with-composer)

Open your terminal and run the following commands:

```
composer require digitalpulsebe/craft-multi-translator -w && php craft plugin/install multi-translator
```

when using DDEV:

```
ddev composer require digitalpulsebe/craft-multi-translator -w && ddev exec php craft plugin/install multi-translator
```

Translation Services
--------------------

[](#translation-services)

For now, we support these API services:

- Deepl - Create your account at [Deepl](https://www.deepl.com/nl/pro-api) to get an API Key
    - with support for [Glossaries](#manage-glossaries)
- Google Cloud Translation v2 - Create an API key in your [Cloud Console](https://console.cloud.google.com/)
- Google Cloud Translation v3 - Create a Service Account your [Cloud Console](https://console.cloud.google.com/iam-admin/serviceaccounts)
- OpenAI (ChatGPT) - Create an API key in at [OpenAI](https://platform.openai.com/)
    - also supports any [OpenAI-compatible endpoint](#openai-compatible-providers) (Azure OpenAI, Ollama, Groq, Mistral, Infomaniak AI Service, …)

### OpenAI-compatible providers

[](#openai-compatible-providers)

The **OpenAI API** provider supports a configurable base URL and custom model name, allowing you to point it at any OpenAI-compatible endpoint.

SettingDescriptionDefault**API Base URL**Base URL of the API endpoint`https://api.openai.com/v1`**API model**Model dropdown, or select "Custom" to enter a free-text model identifier`gpt-4o`**Custom model name**Free-text model identifier (shown when "Custom" is selected)—Both the base URL and model fields support Craft environment variables (e.g. `$OPENAI_BASE_URL`).

#### Example: Infomaniak AI Service

[](#example-infomaniak-ai-service)

In your `.env`:

```
OPENAI_BASE_URL="https://api.infomaniak.com/2/ai/{product_id}/openai/v1"
OPENAI_API_KEY="your-infomaniak-api-token"
```

In the plugin settings: set Base URL to `$OPENAI_BASE_URL`, API Key to `$OPENAI_API_KEY`, select **Custom** as the model and enter e.g. `mistral3` or `qwen3`.

Roadmap
-------

[](#roadmap)

Please let us know which API's and features are desired for this plugin!

Plugin Settings
---------------

[](#plugin-settings)

Configure options in the Craft control panel settings

[![Screenshot](resources/img/screenshot_settings.png)](resources/img/screenshot_settings.png)

Permissions
-----------

[](#permissions)

For non-admin users, enable permissions under the 'Multi Translator' section:

- 'Manage settings'
    - this will allow CMS users to enter an API key an edit general settings
- 'Translate Content'
    - enables the element actions to [translate one-by-one](#translate-one-by-one)
- 'Translate Content in bulk (element action)'
    - enables the bulk actions to [translate one-by-one](#translate-in-bulk)

Supported field types
---------------------

[](#supported-field-types)

- craft\\fields\\PlainText
- craft\\fields\\Table
- craft\\redactor\\Field
- craft\\ckeditor\\Field
- craft\\fields\\Link
- verbb\\vizy\\fields\\VizyField
- craft\\fields\\Matrix (recursive)
- benf\\neo\\Field (recursive)
- verbb\\supertable\\fields\\SuperTableField (recursive)
- verbb\\hyper\\fields\\HyperField
- nystudio107\\seomatic\\fields\\SeoSettings
- ether\\seo\\fields\\SeoField

Usage
-----

[](#usage)

There are two ways to trigger a translation.

### Translate one-by-one

[](#translate-one-by-one)

1. Navigate to the entry on the desired source site/language.
2. Use the buttons in the sidebar and select the target language.

(you can reverse this flow by setting the default direction in the configuration)

[![Screenshot](resources/img/screenshot_sidebar.png)](resources/img/screenshot_sidebar.png)

### Translate in bulk

[](#translate-in-bulk)

1. Navigate to overview of entries you want to get translated.
2. Select the entries in the source language.
3. Use the 'Translate to' dropdown in the actions bar and choose the target language
4. A queue job will be started

[![Screenshot](resources/img/screenshot_actions.png)](resources/img/screenshot_actions.png)

### Manage Glossaries

[](#manage-glossaries)

When using DeepL API, you can add a glossary for [supported language pairs](https://developers.deepl.com/docs/api-reference/glossaries). When translating, the plugin will search a glossary for the appropriate source and target language. There can only be **one glossary for each language pair**.

[![Screenshot](resources/img/screenshot_glossaries.png)](resources/img/screenshot_glossaries.png)

Extending with events
---------------------

[](#extending-with-events)

You can add your own logic by listening to these events:

### The `beforeElementTranslation` event

[](#the-beforeelementtranslation-event)

Example:

```
use digitalpulsebe\craftmultitranslator\events\ElementTranslationEvent;
use digitalpulsebe\craftmultitranslator\services\TranslateService;

Event::on(
    TranslateService::class,
    TranslateService::EVENT_BEFORE_ELEMENT_TRANSLATION,
    function (ElementTranslationEvent $event) {
        $sourceElement = $event->sourceElement;
        $sourceSite = $event->sourceSite;
        $targetSite = $event->targetSite;

        if ($sourceSite->handle == 'disabledSite') {
            $event->isValid = false; // cancel translation for this scenario
        }
    }
);
```

### The `afterElementTranslation` event

[](#the-afterelementtranslation-event)

Example:

```
use digitalpulsebe\craftmultitranslator\events\ElementTranslationEvent;
use digitalpulsebe\craftmultitranslator\services\TranslateService;
use digitalpulsebe\craftmultitranslator\MultiTranslator;

Event::on(
    TranslateService::class,
    TranslateService::EVENT_AFTER_ELEMENT_TRANSLATION,
    function (ElementTranslationEvent $event) {
        $event->targetElement->slug = MultiTranslator::getInstance()->translate->translateText(
            $event->sourceSite->language,
            $event->targetSite->language,
            $event->sourceElement->slug
        );
    }
);
```

### The `beforeFieldTranslation` event

[](#the-beforefieldtranslation-event)

Example:

```
use digitalpulsebe\craftmultitranslator\events\FieldTranslationEvent;
use digitalpulsebe\craftmultitranslator\services\TranslateService;

Event::on(
    TranslateService::class,
    TranslateService::EVENT_BEFORE_FIELD_TRANSLATION,
    function (FieldTranslationEvent $event) {
        if ($event->field->handle == 'fieldTable') {
            $event->isValid = false; // cancel translation for this field
        }
    }
);
```

### The `afterFieldTranslation` event

[](#the-afterfieldtranslation-event)

Example:

```
use digitalpulsebe\craftmultitranslator\events\FieldTranslationEvent;
use digitalpulsebe\craftmultitranslator\services\TranslateService;

Event::on(
    TranslateService::class,
    TranslateService::EVENT_AFTER_FIELD_TRANSLATION,
    function (FieldTranslationEvent $event) {
        if ($event->field->handle == 'body') {
            $event->translatedValue = 'CUSTOM VALUE';
        }
    }
);
```

### Registering a custom API provider

[](#registering-a-custom-api-provider)

The `EVENT_REGISTER_API_PROVIDERS` event on `TranslateService` lets you add your own translation provider without forking the plugin. This makes it possible to integrate services like Gemini, Claude, Azure AI, or any internal translator.

#### 1. Create a provider class

[](#1-create-a-provider-class)

Extend `digitalpulsebe\craftmultitranslator\providers\Provider` and implement the required methods:

```
