PHPackages                             rainlab/translate-plugin - 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. rainlab/translate-plugin

ActiveOctober-plugin

rainlab/translate-plugin
========================

Translate plugin for October CMS

v2.2.15(1mo ago)12666.5k↓31.3%89[5 issues](https://github.com/rainlab/translate-plugin/issues)8MITPHPPHP &gt;=8.0.2

Since Jul 15Pushed 1mo ago14 watchersCompare

[ Source](https://github.com/rainlab/translate-plugin)[ Packagist](https://packagist.org/packages/rainlab/translate-plugin)[ Docs](https://octobercms.com/plugin/rainlab-translate)[ RSS](/packages/rainlab-translate-plugin/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (4)Versions (65)Used By (8)

Translation plugin
==================

[](#translation-plugin)

Enables multi-lingual sites.

Selecting a Language
--------------------

[](#selecting-a-language)

Different languages can be set up in the admin panel using the **Settings → Sites** area. Each site should use a different locale to be considered a language.

The visitor can select a language by prefixing the language code to the URL or using a dedicated hostname. For example:

- `http://website.tld/` will display the site in the default language
- `http://website.tld/ru/` will display the site in Russian
- `http://website.tld/fr/` will display the site in French

### License

[](#license)

This plugin is an official extension of the October CMS platform and is free to use if you have a platform license. See [EULA license](LICENSE.md) for more details.

### Installation

[](#installation)

To install using October CMS v3.1 or above:

```
php artisan plugin:install rainlab.translate

```

To install using October CMS v3.0 and below:

```
php artisan plugin:install rainlab.translate --want="^1.0"

```

### Upgrading from v1 to v2

[](#upgrading-from-v1-to-v2)

If you are upgrading from version 1 of this plugin, [view the upgrade guide](https://github.com/rainlab/translate-plugin/blob/master/UPGRADE.md).

Language Picker Component
-------------------------

[](#language-picker-component)

A visitor can select their chosen language using the native `SitePicker` component that is included in the October CMS core. This component will display a simple dropdown that changes the page language depending on the selection.

```
title = "Home"
url = "/"

[sitePicker]
==

{{ 'Please select your language:'|_ }}

    {% for site in sitePicker.sites %}
        {{ site.name }}
    {% endfor %}

```

If translated, the text above will appear as whatever language is selected by the user. The dropdown is very basic and is intended to be restyled. A simpler example might be:

```

    Switch language to:

    {% for site in sitePicker.sites %}
        {{ site.name }}
    {% endfor %}

```

Message Translation
-------------------

[](#message-translation)

Message or string translation is the conversion of adhoc strings used throughout the site. A message can be translated with parameters.

```
{{ 'site.name'|_ }}

{{ 'Welcome to our website!'|_ }}

{{ 'Hello :name!'|_({ name: 'Friend' }) }}
```

A message can also be translated for a choice usage.

```
{{ 'There are no apples|There are :number applies!'|__(2, { number: 'two' }) }}
```

Or you set a locale manually by passing a second argument.

```
{{ 'this is always english'|_({}, 'en') }}
```

Themes can provide default values for these messages by defining a `translate` key in the `theme.yaml` file, located in the theme directory.

```
name: My Theme
# [...]

translate:
    en:
        site.name: 'My Website'
        nav.home: 'Home'
        nav.video: 'Video'
        title.home: 'Welcome Home'
        title.video: 'Screencast Video'
```

You may also define the translations in a separate file, where the path is relative to the theme. The following definition will source the default messages from the file **config/lang.yaml** inside the theme.

```
name: My Theme
# [...]

translate: config/lang.yaml
```

This is an example of **config/lang.yaml** file with two languages:

```
en:
    site.name: 'My Website'
    nav.home: 'Home'
    nav.video: 'Video'
    title.home: 'Welcome Home'
hr:
    site.name: 'Moje web stranice'
    nav.home: 'Početna'
    nav.video: 'Video'
    title.home: 'Dobrodošli'
```

You may also define the translations in a separate file per locale, where the path is relative to the theme. The following definition will source the default messages from the file **config/lang-en.yaml** inside the theme for the english locale and from the file **config/lang-fr.yaml** for the french locale.

```
name: My Theme
# [...]

translate:
    en: config/lang-en.yaml
    fr: config/lang-fr.yaml
```

This is an example for the **config/lang-en.yaml** file:

```
site.name: 'My Website'
nav.home: 'Home'
nav.video: 'Video'
title.home: 'Welcome Home'
```

In order to make these default values reflected to your frontend site, go to **Settings -&gt; Translate messages** in the backend and hit **Scan for messages**. They will also be loaded automatically when the theme is activated.

The same operation can be performed with the `translate:scan` artisan command. It may be worth including it in a deployment script to automatically fetch updated messages:

```
php artisan translate:scan
```

Add the `--purge` option to clear old messages first:

```
php artisan translate:scan --purge
```

Content &amp; Mail Template Translation
---------------------------------------

[](#content--mail-template-translation)

This plugin activates a feature in the CMS that allows content &amp; mail template files to use language suffixes, for example:

- **welcome.htm** will contain the content or mail template in the default language.
- **welcome-ru.htm** will contain the content or mail template in Russian.
- **welcome-fr.htm** will contain the content or mail template in French.

Model Translation
-----------------

[](#model-translation)

Models can have their attributes translated by using the `RainLab\Translate\Behaviors\TranslatableModel` behavior and specifying which attributes to translate in the class.

```
class User
{
    public $implement = [
        \RainLab\Translate\Behaviors\TranslatableModel::class
    ];

    public $translatable = ['name'];
}
```

The attribute will then contain the default language value and other language code values can be created by using the `translateContext()` method.

```
$user = User::first();

// Outputs the name in the default language
echo $user->name;

$user->translateContext('fr');

// Outputs the name in French
echo $user->name;
```

You may use the same process for setting values.

```
$user = User::first();

// Sets the name in the default language
$user->name = 'English';

$user->translateContext('fr');

// Sets the name in French
$user->name = 'Anglais';
```

The `lang()` method is a shorthand version of `translateContext()` and is also chainable.

```
// Outputs the name in French
echo $user->lang('fr')->name;
```

This can be useful inside a Twig template.

```
{{ user.lang('fr').name }}
```

There are ways to get and set attributes without changing the context.

```
// Gets a single translated attribute for a language
$user->getAttributeTranslated('name', 'fr');

// Sets a single translated attribute for a language
$user->setAttributeTranslated('name', 'Jean-Claude', 'fr');
```

Theme Data Translation
----------------------

[](#theme-data-translation)

It is also possible to translate theme customisation options. Just mark your form fields with `translatable` property and the plugin will take care about everything else:

```
tabs:
    fields:
        website_name:
            tab: Info
            label: Website Name
            type: text
            default: Your website name
            translatable: true
```

Fallback Attribute Values
-------------------------

[](#fallback-attribute-values)

By default, untranslated attributes will fall back to the default locale. This behavior can be disabled by calling the `noFallbackLocale` method when reading the value.

```
$user = User::first();

$user->noFallbackLocale()->lang('fr');

// Returns NULL if there is no French translation
$user->name;
```

When writing the value, the fallback value is determined when the translated value matches the default value. In these cases, the translated value is considered untranslated and not stored.

LocaleAttributeValueIs StoredentitleHello WorldYes (Default)frtitleHello WorldNodetitleHallo WeltYesFor example, if the `en` default locale stores the message as "Hello World" and the `fr` locale value is also "Hello World", then the `fr` value is not stored. The `fr` value is accessed using the fallback value taken from `en`.

You may disable this behavior by passing the `$transatable` attribute value as an array. The first value is the attribute name, the other values represent options, in this case setting the option `fallback` to `false`.

```
public $translatable = [
    ['title', 'fallback' => false]
];
```

This above definition will force the `title` attribute value to be duplicated and stored across all locales.

Indexed Attributes
------------------

[](#indexed-attributes)

Translatable model attributes can also be declared as an index by passing the `$transatable` attribute value as an array. The first value is the attribute name, the other values represent options, in this case setting the option `index` to `true`.

```
public $translatable = [
    'name',
    ['slug', 'index' => true]
];
```

Once an attribute is indexed, you may use the `transWhere` method to apply a basic query to the model.

```
Post::transWhere('slug', 'hello-world')->first();
```

The `transWhere` method accepts a third argument to explicitly pass a locale value, otherwise it will be detected from the environment.

```
Post::transWhere('slug', 'hello-world', 'en')->first();
```

URL Translation
---------------

[](#url-translation)

Pages in the CMS support translating the URL property. Assuming you have 3 languages set up:

- en: English
- fr: French
- ru: Russian

There is a page with the following content:

```
url = "/contact"

[viewBag]
localeUrl[ru] = "/контакт"
==
Page content
```

The word "Contact" in French is the same so a translated URL is not given, or needed. If the page has no URL override specified, then the default URL will be used. Pages will not be duplicated for a given language.

- /fr/contact - Page in French
- /en/contact - Page in English
- /ru/контакт - Page in Russian
- /ru/contact - 404

### Translating URLs in Twig

[](#translating-urls-in-twig)

The `localeUrl` method will replace the route prefix on a URL from one locale to another. For example, converting the current request URL from `en` to `de`.

```
{{ this.request.url|localeUrl('de') }}
```

The `localePage` will return a translated URL for a CMS page. It takes a locale (first argument) and page parameters (second argument).

```
{{ 'blog/post'|localePage('de', { slug: 'foobar' }) }}
```

URL Parameter Translation
-------------------------

[](#url-parameter-translation)

It's possible to translate URL parameters by listening to the `cms.sitePicker.overrideParams` event, which is fired when discovering language URLs.

```
Event::listen('cms.sitePicker.overrideParams', function($page, $params, $oldSite, $newSite) {
    if ($page->baseFileName == 'your-page-filename') {
        return MyModel::translateParams($params, $oldSite->hard_locale, $newSite->hard_locale);
    }
});
```

In `MyModel`, one possible implementation might look like this:

```
public static function translateParams($params, $oldLocale, $newLocale)
{
    $newParams = $params;
    foreach ($params as $paramName => $paramValue) {
        $records = self::transWhere($paramName, $paramValue, $oldLocale)->first();
        if ($records) {
            $records->translateContext($newLocale);
            $newParams[$paramName] = $records->$paramName;
        }
    }
    return $newParams;
}
```

Query String Translation
------------------------

[](#query-string-translation)

It's possible to translate query string parameters by listening to the `cms.sitePicker.overrideQuery` event, which is fired when switching languages.

```
Event::listen('cms.sitePicker.overrideQuery', function($page, $params, $oldSite, $newSite) {
    if ($page->baseFileName == 'your-page-filename') {
        return MyModel::translateParams($params, $oldSite->hard_locale, $newSite->hard_locale);
    }
});
```

For a possible implementation of the `MyModel::translateParams` method look at the example under `URL parameter translation` from above.

Extend Theme Scan
-----------------

[](#extend-theme-scan)

```
Event::listen('rainlab.translate.themeScanner.afterScan', function (ThemeScanner $scanner) {
    // ...
});
```

Settings Model Translation
--------------------------

[](#settings-model-translation)

It's possible to translate your settings model like any other model. To retrieve translated values use:

```
Settings::instance()->getAttributeTranslated('your_attribute_name');
```

Conditionally Extending Plugins
-------------------------------

[](#conditionally-extending-plugins)

#### Models

[](#models)

It is possible to conditionally extend a plugin's models to support translation by placing an `@` symbol before the behavior definition. This is a soft implement will only use `TranslatableModel` if the Translate plugin is installed, otherwise it will not cause any errors.

```
/**
 * Post Model for the blog
 */
class Post extends Model
{
    // [...]

    /**
     * @var array implement the TranslatableModel behavior softly.
     */
    public $implement = ['@'.\RainLab\Translate\Behaviors\TranslatableModel::class];

    /**
     * @var array translatable attributes, if available.
     */
    public $translatable = ['title'];

    // [...]
}
```

The back-end forms will automatically detect the presence of translatable fields and replace their controls for multilingual equivalents.

User Interface
==============

[](#user-interface)

#### Switching Locales

[](#switching-locales)

Users can switch between locales by clicking on the site selection menu in the backend panel. This will add a `_site_id` query value to the URL, allowing for multiple browser tabs to be used.

###  Health Score

67

—

FairBetter than 100% of packages

Maintenance87

Actively maintained with recent releases

Popularity48

Moderate usage in the ecosystem

Community39

Small or concentrated contributor base

Maturity82

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 51% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~42 days

Recently: every ~161 days

Total

61

Last Release

59d ago

Major Versions

1.x-dev → v2.0.02022-10-05

PHP version history (2 changes)1.6.0PHP &gt;=5.5.9

v2.0.0PHP &gt;=8.0.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/106532?v=4)[october](/maintainers/october)[@october](https://github.com/october)

---

Top Contributors

[![daftspunk](https://avatars.githubusercontent.com/u/1392869?v=4)](https://github.com/daftspunk "daftspunk (295 commits)")[![samgeorges](https://avatars.githubusercontent.com/u/4927493?v=4)](https://github.com/samgeorges "samgeorges (76 commits)")[![mjauvin](https://avatars.githubusercontent.com/u/2013630?v=4)](https://github.com/mjauvin "mjauvin (48 commits)")[![octoberapp](https://avatars.githubusercontent.com/u/6543374?v=4)](https://github.com/octoberapp "octoberapp (31 commits)")[![LukeTowers](https://avatars.githubusercontent.com/u/7253840?v=4)](https://github.com/LukeTowers "LukeTowers (17 commits)")[![acasar](https://avatars.githubusercontent.com/u/6329543?v=4)](https://github.com/acasar "acasar (14 commits)")[![gergo85](https://avatars.githubusercontent.com/u/2959112?v=4)](https://github.com/gergo85 "gergo85 (13 commits)")[![modmac](https://avatars.githubusercontent.com/u/6702425?v=4)](https://github.com/modmac "modmac (9 commits)")[![bennothommo](https://avatars.githubusercontent.com/u/15900351?v=4)](https://github.com/bennothommo "bennothommo (7 commits)")[![munxar](https://avatars.githubusercontent.com/u/12279988?v=4)](https://github.com/munxar "munxar (6 commits)")[![tobias-kuendig](https://avatars.githubusercontent.com/u/8600029?v=4)](https://github.com/tobias-kuendig "tobias-kuendig (6 commits)")[![justin-lau](https://avatars.githubusercontent.com/u/2094881?v=4)](https://github.com/justin-lau "justin-lau (5 commits)")[![vannut](https://avatars.githubusercontent.com/u/7419859?v=4)](https://github.com/vannut "vannut (5 commits)")[![alekseybobkov](https://avatars.githubusercontent.com/u/481606?v=4)](https://github.com/alekseybobkov "alekseybobkov (4 commits)")[![tiipiik](https://avatars.githubusercontent.com/u/3844253?v=4)](https://github.com/tiipiik "tiipiik (4 commits)")[![aurelien-roy](https://avatars.githubusercontent.com/u/1875998?v=4)](https://github.com/aurelien-roy "aurelien-roy (3 commits)")[![mahony0](https://avatars.githubusercontent.com/u/2674488?v=4)](https://github.com/mahony0 "mahony0 (3 commits)")[![mplodowski](https://avatars.githubusercontent.com/u/6774277?v=4)](https://github.com/mplodowski "mplodowski (3 commits)")[![mariavilaro](https://avatars.githubusercontent.com/u/10522884?v=4)](https://github.com/mariavilaro "mariavilaro (3 commits)")[![Samuell1](https://avatars.githubusercontent.com/u/3110002?v=4)](https://github.com/Samuell1 "Samuell1 (3 commits)")

---

Tags

translateoctoberoctobercms

### Embed Badge

![Health badge](/badges/rainlab-translate-plugin/health.svg)

```
[![Health](https://phpackages.com/badges/rainlab-translate-plugin/health.svg)](https://phpackages.com/packages/rainlab-translate-plugin)
```

###  Alternatives

[rainlab/builder-plugin

Builder plugin for October CMS

17147.2k1](/packages/rainlab-builder-plugin)[rainlab/pages-plugin

Pages plugin for October CMS

12252.4k4](/packages/rainlab-pages-plugin)[rainlab/user-plugin

User plugin for October CMS

11954.3k13](/packages/rainlab-user-plugin)[rainlab/blog-plugin

Blog plugin for October CMS

17257.7k](/packages/rainlab-blog-plugin)[rainlab/sitemap-plugin

Sitemap plugin for October CMS

2277.8k1](/packages/rainlab-sitemap-plugin)[janvince/smallcontactform

Simple but flexible multi language contact form builder with custom fields, validation and passive antispam

307.4k](/packages/janvince-smallcontactform)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
