PHPackages                             tuzelko/yii2-localizable - 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. tuzelko/yii2-localizable

ActiveYii2-extension[Localization &amp; i18n](/categories/localization)

tuzelko/yii2-localizable
========================

Localization extension for Yii2 framework

v1.0.0(1mo ago)088MITPHPPHP &gt;=8.0CI passing

Since Jun 6Pushed 1mo agoCompare

[ Source](https://github.com/TuzelKO/yii2-localizable)[ Packagist](https://packagist.org/packages/tuzelko/yii2-localizable)[ RSS](/packages/tuzelko-yii2-localizable/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

Yii2 Localization extension
===========================

[](#yii2-localization-extension)

[![Project Status: Active](https://camo.githubusercontent.com/39c688bf243eeb6d3bfc529dcf3cb27443613deb696c8fa9f49bccf1e63e3bef/68747470733a2f2f7777772e7265706f7374617475732e6f72672f6261646765732f6c61746573742f6163746976652e737667)](https://www.repostatus.org/#active)[![Tests](https://github.com/TuzelKO/yii2-localizable/actions/workflows/tests.yml/badge.svg)](https://github.com/TuzelKO/yii2-localizable/actions/workflows/tests.yml)[![Latest Version](https://camo.githubusercontent.com/817c5a8e3adef757c096a4e65124542519839faa6353b07fa9a3e1fd8010b8e3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f74757a656c6b6f2f796969322d6c6f63616c697a61626c65)](https://packagist.org/packages/tuzelko/yii2-localizable)[![PHP Version](https://camo.githubusercontent.com/e27d04e13f8bb33bd1270e65edd04bdb1d8478d50b7335ca1eec8a40d033d8b8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f74757a656c6b6f2f796969322d6c6f63616c697a61626c652f706870)](https://packagist.org/packages/tuzelko/yii2-localizable)[![Total Downloads](https://camo.githubusercontent.com/dca8c20044f90d69ade1cda58f63d545f3a32f07450374ef07958622954cdfcd/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f74757a656c6b6f2f796969322d6c6f63616c697a61626c65)](https://packagist.org/packages/tuzelko/yii2-localizable)[![License](https://camo.githubusercontent.com/146545517fe8556dc23fc85d93f72c775a403dcd6fa664dfc8c3f94d0049c2c8/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f54757a656c4b4f2f796969322d6c6f63616c697a61626c65)](https://github.com/TuzelKO/yii2-localizable/blob/main/LICENSE)

Localization extension for the [Yii2](https://www.yiiframework.com/) framework.

Stores localized attributes in a per-language related table (one row per language) instead of a JSON column, so the values stay plain, indexable columns you can search and sort on. A single behavior virtualizes those columns on the owner model, and a matching validator drops localization payloads straight into a form's `rules()`.

Features
--------

[](#features)

- **Real columns, not JSON** — each translation is an ordinary column, so it is searchable, sortable, and indexable
- **Virtualized attributes** — `$post->title` resolves for `Yii::$app->language` with automatic fallback to the default language
- **Single read/write virtual** — get/set the whole `{default, values}` structure through one `localization` property
- **Self-managing rows** — the behavior rebuilds the language rows on save and removes them on (hard) delete
- **Form-ready validation** — `LocalizationValidator` reuses the row model's own rules; no duplicated field config
- **Translated messages out of the box** — validation messages ship translated into many languages (the full Yii2 locale set), auto-registered via the extension bootstrap
- **Soft-delete friendly** — soft-deleting an owner keeps its localizations (recoverable); only a hard delete clears them

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

[](#requirements)

- PHP &gt;= 8.0
- yiisoft/yii2 ~2.0

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

[](#installation)

```
composer require tuzelko/yii2-localizable
```

How it works
------------

[](#how-it-works)

The owner model holds no localized columns itself. Instead it owns a `hasMany` relation to a *localization* table that carries one row per language:

Owner (`post`)Localization (`post_localization`)`id``id`, `post_id`, `lang`, `is_default`, `title`, `description`, …Exactly one row per owner is flagged `is_default` — that language is the fallback when the current one is missing. The behavior reads and writes that whole set through a single virtual property.

Quick start
-----------

[](#quick-start)

### 1. Create the tables in your migration

[](#1-create-the-tables-in-your-migration)

```
$this->createTable('{{%post}}', [
    'id' => $this->primaryKey(),
    // ... your non-localized columns
]);

$this->createTable('{{%post_localization}}', [
    'id'          => $this->primaryKey(),
    'post_id'     => $this->integer()->notNull(),
    'lang'        => $this->string(5)->notNull(),
    'is_default'  => $this->boolean()->notNull()->defaultValue(false),
    'title'       => $this->string()->notNull(),
    'description' => $this->text()->null(),
]);

$this->addForeignKey('fk_post_localization_post', '{{%post_localization}}', 'post_id', '{{%post}}', 'id', 'CASCADE');
$this->createIndex('idx_post_localization_post_lang', '{{%post_localization}}', ['post_id', 'lang'], true);
```

### 2. The localization row model

[](#2-the-localization-row-model)

Its `rules()` are the single source of field validation — the behavior runs the payload against them.

```
use yii\db\ActiveRecord;

class PostLocalization extends ActiveRecord
{
    public static function tableName(): string { return 'post_localization'; }

    public function rules(): array
    {
        return [
            [['lang', 'title'], 'required'],
            [['lang'], 'string', 'max' => 5],
            [['title'], 'string', 'max' => 255],
            [['description'], 'string'],
        ];
    }
}
```

### 3. Attach the behavior to the owner

[](#3-attach-the-behavior-to-the-owner)

The behavior reads the row class and FK link off the relation, so the relation must be a plain FK `hasMany`.

```
use tuzelko\yii\localization\LocalizableBehavior;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;

class Post extends ActiveRecord
{
    public static function tableName(): string { return 'post'; }

    public function behaviors(): array
    {
        return [
            'localizable' => [
                'class'      => LocalizableBehavior::class,
                'attributes' => ['title', 'description'],
            ],
        ];
    }

    public function getLocalizations(): ActiveQuery
    {
        return $this->hasMany(PostLocalization::class, ['post_id' => 'id']);
    }
}
```

That's it. `$post->title` is now a virtual, language-aware attribute, and `$post->localization` reads/writes the whole structure.

Reading localized attributes
----------------------------

[](#reading-localized-attributes)

```
$post = Post::findOne(1);

Yii::$app->language = 'ru';
$post->title;        // Russian title

Yii::$app->language = 'de'; // no German row
$post->title;        // falls back to the default language
```

The structured form is available through the singular `localization` virtual:

```
$post->localization;
// [
//     'default' => 'en',
//     'values'  => [
//         'en' => ['title' => 'Hello', 'description' => 'World'],
//         'ru' => ['title' => 'Привет', 'description' => 'Мир'],
//     ],
// ]
```

> The singular `localization` is the structured object; the plural `localizations` relation is the raw row collection (use it for eager loading).

Writing localizations
---------------------

[](#writing-localizations)

Assign the `{default, values}` structure and save — the behavior rebuilds the rows in a transaction and sets `is_default` on the default language:

```
$post = new Post();
$post->localization = [
    'default' => 'en',
    'values'  => [
        'en' => ['title' => 'Hello', 'description' => 'World'],
        'ru' => ['title' => 'Привет', 'description' => 'Мир'],
    ],
];
$post->save();
```

Saving again with a different structure replaces the whole set. A new owner with no localizations assigned fails validation — at least one localization is required.

Validation in forms
-------------------

[](#validation-in-forms)

Drop `LocalizationValidator` into a form's `rules()`, pointing it at a localizable model. It pulls the row class and field rules off that model's behavior, so the form restates neither:

```
use tuzelko\yii\localization\LocalizationValidator;
use yii\base\Model;

class PostForm extends Model
{
    public $localization;

    public function rules(): array
    {
        return [
            [['localization'], 'required'],
            [['localization'], LocalizationValidator::class, 'model' => new Post()],
        ];
    }
}
```

Being a plain rule, it keeps the usual knobs (`on` / `when` / `skipOnEmpty` / `message`). Field-level violations (a too-long or missing `title`) surface as validation errors (→ 400) instead of a save-time exception (→ 500).

Translated messages
-------------------

[](#translated-messages)

The bundled validation messages ship translated into many languages — the full Yii2 locale set. The extension's composer bootstrap registers the `localization` message category automatically, so they work without any config in the consuming app — `Yii::$app->language` is all that matters.

Need to override a message or add a language? Define your own `localization` translation in the app config; the bootstrap never overwrites an existing one:

```
'components' => [
    'i18n' => [
        'translations' => [
            'localization' => [
                'class'    => \yii\i18n\PhpMessageSource::class,
                'basePath' => '@app/messages',
            ],
        ],
    ],
],
```

Deletion behaviour
------------------

[](#deletion-behaviour)

The behavior owns the localization rows end to end. **Hard-deleting** an owner removes its rows automatically (hook it once, never forget cleanup). **Soft-delete** does not fire the delete event, so a soft-deleted owner keeps its localizations and they come back on restore.

Running tests
-------------

[](#running-tests)

```
make test
```

Tests run inside Docker (PHP 8.0 + SQLite) with no local setup required.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance90

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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

Unknown

Total

1

Last Release

49d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/38176435?v=4)[Eugene Frost](/maintainers/TuzelKO)[@TuzelKO](https://github.com/TuzelKO)

---

Top Contributors

[![TuzelKO](https://avatars.githubusercontent.com/u/38176435?v=4)](https://github.com/TuzelKO "TuzelKO (1 commits)")

---

Tags

localizationi18ntranslationyii2extensionBehavior

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/tuzelko-yii2-localizable/health.svg)

```
[![Health](https://phpackages.com/badges/tuzelko-yii2-localizable/health.svg)](https://phpackages.com/packages/tuzelko-yii2-localizable)
```

###  Alternatives

[omgdef/yii2-multilingual-behavior

Port of the yii-multilingual-behavior for yii

143182.6k9](/packages/omgdef-yii2-multilingual-behavior)[skeeks/cms

SkeekS CMS — control panel and tools based on php framework Yii2

13725.8k63](/packages/skeeks-cms)[geertw/yii2-translatable-url-rule

A custom URL rule class for Yii 2 which allows to create translated URL rules

1421.9k](/packages/geertw-yii2-translatable-url-rule)[lav45/yii2-translated-behavior

This extension allows you to quickly and simple enough to add translations for any ActiveRecord models.

3742.6k](/packages/lav45-yii2-translated-behavior)[uran1980/yii2-translate-panel

Yii2 Translate Panel makes the translation of your application awesome!

2518.3k1](/packages/uran1980-yii2-translate-panel)[yiimaker/yii2-translatable

Translatable behavior aggregates logic of linking translations to the primary model

1535.5k1](/packages/yiimaker-yii2-translatable)

PHPackages © 2026

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