PHPackages                             fsi/translatable - 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. fsi/translatable

ActiveLibrary[Localization &amp; i18n](/categories/localization)

fsi/translatable
================

A library for handling translations

1.2.0(4mo ago)011.3k↓35.7%2[2 issues](https://github.com/fsi-open/translatable/issues)2proprietaryPHPPHP ^8.1CI passing

Since Aug 16Pushed 4mo ago4 watchersCompare

[ Source](https://github.com/fsi-open/translatable)[ Packagist](https://packagist.org/packages/fsi/translatable)[ RSS](/packages/fsi-translatable/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (43)Versions (18)Used By (2)

Translatable
============

[](#translatable)

This components serves to provide a way of creating translatable objects for multiple languages. There are two main concepts governing this idea - a translatable and a translation. A translatable is an object whose data is stored in a collection of translation objects, each per a separate locale. Each of these has a separate configuration object, where all the necessary information about the relation between the two is stored. These are `TranslatableConfiguration`and `TranslationConfiguration` and can be retrieved via `ConfigurationResolver`.

The lifecycle of a translatable object is handled by a set of dedicated classes in the `Entity` directory and these are `TranslationLoader`, `TranslationUpdater` and `TranslationCleaner`. They will:

- set the current locale for the translatable object,
- load data from a translation (if one exists),
- create a new translation or update the existing one,
- remove empty translation objects.

However they will not work on their own and need to be hooked up to whatever storage mechanism you are using (ORM, ODM etc.) through a subscriber(s). Currently there is out-of-the-box integration only for Doctrine / Symfony combination.

What can be a translatable field?
---------------------------------

[](#what-can-be-a-translatable-field)

When it comes to storage, mapping/configuration for all of the below should be included in the translation entity files, not the translatable.

### By default

[](#by-default)

- scalar values (string, integer, float),
- objects (though their storage will probably need to be handled through some integration),
- `WebFile` objects from `fsi/files` component will work and be persisted/removed along with the translation entity,

### with Doctrine

[](#with-doctrine)

- embeddables (also nested), although embeddables themselves cannot have translations due to not having an identifier,
- one-to-one relations,
- collection relations,

Example entities
----------------

[](#example-entities)

Let us consider an example of a translatable `Article` entity with `ArticleTranslation` translation:

```
declare(strict_types=1);

namespace Tests\Entity;

use DateTimeImmutable;
use FSi\Component\Translatable\Integration\Doctrine\ORM\ProxyTrait;

class Article
{
    use ProxyTrait;

    private ?int $id = null;
    private ?string $locale = null;
    private ?DateTimeImmutable $publicationDate = null;
    private ?string $title = null;
    private ?string $description = null;

    // getters and setters will probably be required for whatever
    // mechanism you use for modifying the object, though are not
    // required by the component itself
}

declare(strict_types=1);

namespace Tests\Entity;

class ArticleTranslation
{
    private ?int $id = null;
    private ?string $locale = null;
    private ?string $title = null;
    private ?string $description = null;
    private ?Article $article;

    // getters and setters are not required
}
```

As you can see, they are almost a mirror of each other, aside for the `$publicationDate` field in the translatable and `$article` field in the translation. That is because `$publicationDate`is not translatable field (it is stored directly in the translatable object) and `$article` serves as a way to bind the translation to the translatable. Both fields have the `locale` field: the translatable stores the current locale and the translation has the locale it was created for.

In order for the component to recognize these objects as the translatable-translation duo, you will need to define their configurations. When using Symfony, this can be achieved as simply as this:

```
fsi_translatable:
    entities:
        Tests\Entity\Article:
            localeField: locale # this can be skipped for a default value of locale
            fields: [title, description]
            disabledAutoTranslationsUpdate: false # optional and false by default
            translation:
                localeField: locale # also can be skipped
                class: Tests\FSi\ArticleTranslation
                relationField: article
```

**IMPORTANT**

- Both objects need to have the same fields present. It is not possible to map translatable fields to different fields in the translation entity.
- Translation objects **cannot** have required constructor arguments.

If you want to create your configuration manually, you will need to provide the `ConfigurationResolver` with a collection of `TranslatableConfiguration` objects with the necessary data.

Usage (Doctrine + Symfony)
--------------------------

[](#usage-doctrine--symfony)

Unless you load your bundles and configuartion directly in the Kernel class, you will need to load the Translatable bundle in the `config/bundles.php`:

```
return [
    // Doctrine and Symfony bundles
    FSi\Component\Translatable\Integration\Symfony\TranslatableBundle::class => ['all' => true]
];
```

and then add a `config/packages/fsi_translatable.yaml` file:

```
fsi_translatable:
    entities:
        # configuration for specific entities, see above for an example
```

You can of course load the configuration manually through PHP, but XML configuration is not supported at the moment.

After that the component will mostly behave automatically. When creating a new translatable object with any of the translatable fields filled, the current locale (via `LocaleProvider` object) will be fetched, a new instance of translation will be created and then the relevant contents of the translatable object will be copied into it. On subsequent loading of the translatable object, the data will be loaded back from the stored translation. Any modifications to the translatable fields in the translatable object will update the existing translation automatically. Should the locale provided by the `LocaleProvider` be different, a new translation will be created.

If for some reason you need to fetch single/all translation objects directly, you can do so via the `TranslationProvider`.

If a translatable object is removed, all the translations will be cleared throught Doctrine's entity manager (via `TranslationManager`), so anysubscribers listening on the translation entity's lifecycle events will be fired as well.

**IMPORTANT**

- If you have translatable collection fields, you **need** to initialize them in the translation object's constructor.

### Locale fetching and persisting

[](#locale-fetching-and-persisting)

The `LocaleProvider` implementation for Symfony will try to fetch the locale from three sources:

1. It will check the for a persisted locale (more on that later).
2. Failing to find one, it will fetch a current `Request` object from a `RequestStack` and retrieve the locale from that.
3. Should there be no current `Request` (this will be the case for console commands and some test environments), it will return the default locale from the `FrameworkBundle`.

If you want to manually set the locale that is returned from the `LocaleProvider`, calling the `LocaleProvider::saveLocale()`method will persist it in the service until the next request. You can allso manually call the `LocaleProvider::resetSavedLocale()`to clear it.

### Disabling automatic translations updates

[](#disabling-automatic-translations-updates)

If you prefer creating translations manually and do not want them overwritten with contents of the translatable entity, you can set the `disableAutoTranslationsUpdate`option in it's configuration to `true`. This will prevent any creation or update of translations during the flush operation, but will still populate the translatable entity with contents of a translation entity, if one exists for the current locale.

###  Health Score

50

—

FairBetter than 96% of packages

Maintenance77

Regular maintenance activity

Popularity26

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

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 ~100 days

Recently: every ~199 days

Total

17

Last Release

126d ago

Major Versions

0.5.1 → 1.0.02023-10-19

PHP version history (2 changes)0.1PHP ^7.4|^8.0

1.1.0PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/d59f4fb3ce9d43f785d44957fc66c4ac950c7b758469a987dc90369bf845fdcc?d=identicon)[chives](/maintainers/chives)

---

Top Contributors

[![szymach](https://avatars.githubusercontent.com/u/2230556?v=4)](https://github.com/szymach "szymach (33 commits)")[![chives](https://avatars.githubusercontent.com/u/1170578?v=4)](https://github.com/chives "chives (30 commits)")[![rn0](https://avatars.githubusercontent.com/u/15381?v=4)](https://github.com/rn0 "rn0 (19 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/fsi-translatable/health.svg)

```
[![Health](https://phpackages.com/badges/fsi-translatable/health.svg)](https://phpackages.com/packages/fsi-translatable)
```

###  Alternatives

[joedixon/laravel-translation

A tool for managing all of your Laravel translations

717911.4k11](/packages/joedixon-laravel-translation)[illuminate/translation

The Illuminate Translation package.

6936.4M495](/packages/illuminate-translation)[lajax/yii2-translate-manager

Translation management extension for Yii 2

227578.8k13](/packages/lajax-yii2-translate-manager)[larswiegers/laravel-translations-checker

Make sure your laravel translations are checked and are included in all languages.

256423.2k2](/packages/larswiegers-laravel-translations-checker)[inpsyde/multilingual-press

Simply THE multisite-based free open source plugin for your multilingual websites.

2414.0k1](/packages/inpsyde-multilingual-press)[statikbe/laravel-chained-translator

The Laravel Chained Translator can combine several translators that can override each others translations.

36149.4k6](/packages/statikbe-laravel-chained-translator)

PHPackages © 2026

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