PHPackages                             daoandco/shadow-translate - 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. daoandco/shadow-translate

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

daoandco/shadow-translate
=========================

Manage translated content with shadow tables

1.0.0(9y ago)06MITPHP

Since Feb 27Pushed 9y ago2 watchersCompare

[ Source](https://github.com/DaoAndCo/cakephp-shadow-translate)[ Packagist](https://packagist.org/packages/daoandco/shadow-translate)[ RSS](/packages/daoandco-shadow-translate/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (1)Dependencies (5)Versions (12)Used By (0)

Shadow translate
================

[](#shadow-translate)

[![Build Status](https://camo.githubusercontent.com/12d4cfd1d95b2a0085679c8d4d44934101b87d6283257579933980f056bb8160/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f4144377369782f63616b657068702d736861646f772d7472616e736c6174652f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://travis-ci.org/AD7six/cakephp-shadow-translate)[![Coverage Status](https://camo.githubusercontent.com/6d41bd58bcbb287d6ce6e98025ccb6aae8e6120609ea583db6d131be89f3c3a4/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f4144377369782f63616b657068702d736861646f772d7472616e736c6174652f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://coveralls.io/r/AD7six/cakephp-shadow-translate)[![Total Downloads](https://camo.githubusercontent.com/f3c6719475e8e4e60197dfb635766a23382d21f8aa15e2c19f66887161a12c3f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6164377369782f736861646f772d7472616e736c6174652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ad7six/shadow-translate)[![License](https://camo.githubusercontent.com/942e017bf0672002dd32a857c95d66f28c5900ab541838c6c664442516309c8a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265)](LICENSE.txt)

This plugin uses a shadow table for translated content, instead of the core's more flexible (but also potentially quite inefficient) EAV-style translate behavior. The shadow translate behavior is designed to have the same API as the core's translate behavior making it a drop-in replacement in terms of usage.

Quickstart
----------

[](#quickstart)

First install the plugin for your app using composer:

`php composer.phar require ad7six/shadow-translate`

Load the plugin by adding following statement to your app's `config/bootstrap.php`:

```
Plugin::load('ShadowTranslate');
```

The shadow translate behavior expects each table to have its own translation table. Taking the blog tutorial as a start point, the following table would already exist:

```
CREATE TABLE posts (
	id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
	title VARCHAR(50),
	body TEXT,
	created DATETIME DEFAULT NULL,
	modified DATETIME DEFAULT NULL
);
```

To prepare for using the shadow translate behavior, the following table would be created:

```
CREATE TABLE posts_translations (
	id INT UNSIGNED,
	locale VARCHAR(5),
	title VARCHAR(50),
	body TEXT,
	PRIMARY KEY (id, locale)
);
```

Note that the id is the same type as the posts table - but the primary key is a compound key using both id and locale.

Usage is very similar to the core's behavior so e.g.:

```
class PostsTable extends Table {

	public function initialize(array $config) {
		$this->addBehavior('ShadowTranslate.ShadowTranslate');
	}
}
```

You can specify the fields in the translation table - but if you don't they are derived from the translation table schema. From this point forward, see [the documentation for the core translate behavior](http://book.cakephp.org/3.0/en/orm/behaviors/translate.html), the shadow translate behavior should act the same, and if it doesn't, well, see below.

Why use Shadow Translate
------------------------

[](#why-use-shadow-translate)

The standard translate behavior uses an [EAV](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model)style translations table, and one join per field. By default all translations are stored in [the same translation table](https://github.com/cakephp/app/blob/master/config/schema/i18n.sql). To give an example, the core translation behavior generates sql of the form:

```
SELECT
    posts.*,
    posts_title_translations.title,
    posts_title_translations.content,
    etc.
FROM
    posts
LEFT JOIN
    i18n as posts_title_translations ON (
        posts_title_translations.locale = "xx" AND
        posts_title_translations.model = "Posts" AND
        posts_title_translations.foreign_key = posts.id AND
        posts_title_translations.field = 'title'
   )
LEFT JOIN
    i18n as posts_body_translations ON (
        posts_body_translations.locale = "xx" AND
        posts_body_translations.model = "Posts" AND
        posts_body_translations.foreign_key = posts.id AND
        posts_body_translations.field = 'body'
   )
etc.
```

There is very little setup for the core translate behavior, but the cost for no-setup is sql complexity, and it is more complex for each translated field. Depending on how much data there is being translated - it's quite possible for this data structure to cause slow queries; it also complicates finding records by translated field values.

Key points:

- Easy to setup
- All translated content stored in the same table
- One join per translated field when querying
- Less efficient queries - more joins and one index for all content
- Harder to find by translated content

By contrast, the shadow translate behavior does not use an EAV style translation table, the translations are stored in a *copy* of the main data table. This permits much less complex sql at the cost of having *some* setup steps per table. The shadow translate behavior generates sql of the form:

```
SELECT
    posts.*,
    posts_translations.*
FROM
    posts
LEFT JOIN
    posts_translations ON (
        posts_translations.locale = "xx" AND
        posts_translations.id = posts.id
)
// no etc.
```

Key points:

- (Slightly) more work to setup
- Translated content is stored in a copy of the main data table
- One join per translated table
- More efficient queries - less joins and indexes per translated field are possible
- Easier to find by translated content

Roadmap
-------

[](#roadmap)

The initial release is only the behavior, it is planned for the future to add:

- A shell to create shadow tables (migration based)
- A shell to import from the core's i18n table
- A shell to export to the core's i18n table

Bugs
----

[](#bugs)

Most likely!

If you happen to stumble upon a bug, please feel free to create a pull request with a fix (optionally with a test), and a description of the bug and how it was resolved.

You can also create an issue with a description to raise awareness of the bug.

Support / Questions
-------------------

[](#support--questions)

If you have a problem, if no one else can help, and if you can find them, maybe you can find help on IRC in the #FriendsOfCake channel.

###  Health Score

29

—

LowBetter than 57% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 80.6% 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 ~93 days

Recently: every ~139 days

Total

9

Last Release

3392d ago

Major Versions

0.5.3 → 1.0.02017-03-17

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/5025797?v=4)[Flavien Beninca](/maintainers/ozee31)[@ozee31](https://github.com/ozee31)

---

Top Contributors

[![AD7six](https://avatars.githubusercontent.com/u/33387?v=4)](https://github.com/AD7six "AD7six (150 commits)")[![ADmad](https://avatars.githubusercontent.com/u/142658?v=4)](https://github.com/ADmad "ADmad (19 commits)")[![greew](https://avatars.githubusercontent.com/u/189321?v=4)](https://github.com/greew "greew (8 commits)")[![ozee31](https://avatars.githubusercontent.com/u/5025797?v=4)](https://github.com/ozee31 "ozee31 (3 commits)")[![Orken](https://avatars.githubusercontent.com/u/2988240?v=4)](https://github.com/Orken "Orken (2 commits)")[![segy](https://avatars.githubusercontent.com/u/1355459?v=4)](https://github.com/segy "segy (1 commits)")[![burzum](https://avatars.githubusercontent.com/u/162789?v=4)](https://github.com/burzum "burzum (1 commits)")[![joshk](https://avatars.githubusercontent.com/u/8701?v=4)](https://github.com/joshk "joshk (1 commits)")[![Noodlex](https://avatars.githubusercontent.com/u/14205390?v=4)](https://github.com/Noodlex "Noodlex (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/daoandco-shadow-translate/health.svg)

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

###  Alternatives

[cakephp/migrations

Database Migration plugin for CakePHP

13812.5M282](/packages/cakephp-migrations)[php-translation/translator

Translator services

25224.8k5](/packages/php-translation-translator)[smmoosavi/php-gettext

Wrapper for php-gettext by danilo segan. This library provides PHP functions to read MO files even when gettext is not compiled in or when appropriate locale is not present on the system.

1926.6k1](/packages/smmoosavi-php-gettext)[laradevs/spanish

labels translated to spanish

166.7k](/packages/laradevs-spanish)

PHPackages © 2026

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