Handles ActiveRecord's attribute translations

Overview

TranslateableBehavior for Yii2

This behavior has been inspired by the great work of Mikehaertl's Translatable Behavior for Yii 1.*.

It eases the translation of ActiveRecord's attributes as it maps theme from a translation table into the main record. It also automatically loads application language by default.

Sample of use:

save(); // change language $tour->language = 'fr'; $tour->title = "French title"; // save translation only $tour->saveTranslation(); ">


// create a record
$tour = new Tour;

$tour->title = "English title";

// save both the new Tour and a related translation record with the title
$tour->save();


// change language
$tour->language = 'fr';

$tour->title = "French title";

// save translation only
$tour->saveTranslation();

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require "2amigos/yii2-translateable-behavior"

or add

"2amigos/yii2-translateable-behavior" : "~1.1"

to the require section of your application's composer.json file.

Usage

Preparation

First you need to move all the attributes that require to be translated into a separated table. For example, imagine we wish to keep translations of title and description from our tour entity. Our schema should result on the following:

    +--------------+        +--------------+        +-------------------+
    |     tour     |        |     tour     |        |      tour_lang    |
    +--------------+        +--------------+        +-------------------+
    |           id |        |           id |        |                id |
    |        title |  --->  |   created_at |   +    |           tour_id |
    |  description |        |   updated_at |        |          language |
    |   created_at |        +--------------+        |             title |
    |   updated_at |                                |       description |
    +--------------+                                +-------------------+

After we have modified our schema, now we need to define a relation in our ActiveRecord object. The following example assumes that we have already created a TourLang model (see the schema above):

/**
* @return \yii\db\ActiveQuery
*/
public function getTranslations()
{
    return $this->hasMany(TourLang::className(), ['tour_id' => 'id']);
}

Finally, we need to attach our behavior.

use dosamigos\translateable\TranslateableBehavior;

\\ ...

public function behaviors()
{
    return [
        'trans' => [ // name it the way you want
            'class' => TranslateableBehavior::className(),
            // in case you named your relation differently, you can setup its relation name attribute
            // 'relation' => 'translations',
            // in case you named the language column differently on your translation schema
            // 'languageField' => 'language',
            'translationAttributes' => [
                'title', 'description'
            ]
        ],
    ];
}

Basic Usage

save(); // change language $tour->language = 'fr'; $tour->title = "French title"; // save fr translation only $tour->saveTranslation();">
// create a record
$tour = new Tour;
$tour->title = "English title";

// save both the new Tour and a related translation record with the title
$tour->save();


// change language
$tour->language = 'fr';

$tour->title = "French title";

// save fr translation only
$tour->saveTranslation();

You may also set multiple translations directly:

"Deutscher Titel", ], ]; // save both the new Tour and a related translation record with the title $tour->save();">
$tour = new Tour;
$tour->title = [
  'translations' => [
     'en' => "English title",
     'de' => "Deutscher Titel",
  ],
];

// save both the new Tour and a related translation record with the title
$tour->save();

Fallback language

In case no translation is available for a specific language the behavior allows to specify a fallback translation to load instead. By default the fallback will use the application source language. It can be configured by setting the fallbackLanguage property of the behavior.

Fallback language can be configured to be a single language or per language:

// use english as fallback for all languages when no translation is available
'fallbackLanguage' => 'en',
// alternatively:
'fallbackLanguage' => [
    'de' => 'en', // fall back to English if German translation is missing
    'uk' => 'ru', // fall back to Russian if no Ukrainian translation is available
],

Additionally to the configurable fallback a fallback to non-localized language is applied automatically. E.g. if no translation exists for de-AT (German in Austria) the translation will fall back to de. The fallback goes further if de is not found using the fallbackLanguage configuration, so from the example above it will then try en.

When the fallback is defined in array format and no fallback can be found for a language, the first fallback is returned.

You may disable the fallback mechanism by setting fallbackLanguage to false.

If you want to configure fallback languages globally, you can do so by configuring the TranslateableBehavior class in Yii DI container:

Yii::$container->set('dosamigos\translateable\TranslateableBehavior', ['fallbackLanguage' => 'de']);

Deleting translations

By default, when an active record is deleted, translation records are deleted in the afterSave event. However some database scenarios require different configuration, in case foreign keys restrict the deletion of records.

You may configure 'deleteEvent' to be either ActiveRecord::EVENT_BEFORE_DELETE or ActiveRecord::EVENT_AFTER_DELETE to control on which event the deletion of records should be performed. You may set 'deleteEvent' to false to disable deletion and rely on DB foreign key cascade or implement your own method.

When using the Translateablebehavior in an ActiveRecord you should enable transactions() for the delete operation.

2amigOS!
Web development has never been so fun!
www.2amigos.us

Comments
  • fixed modelEqualsFallbackTranslation with

    fixed modelEqualsFallbackTranslation with "empty" values edge case

    This PR fixed an edge case we found:

    if skipSavingDuplicateTranslation is set to true and all translationAttributes have values that PHP considers "empty" in empty(), no translation is created even when creating a new record.

    To fix this, in modelEqualsFallbackTranslation() we should only check the presence of $model->$translationAttribute with isset(), not the value with emtpy().

    Our usecase are translation tables in which we store only a translatable active/inactive status attribute of a base model as 0|1 values with default set to 0.

    I also added a test which try to cover these edgeCase and where you can see where the problem can occur.

    Cc: @cebe @schmunk42

    opened by handcode 10
  • Feature: fallback language

    Feature: fallback language

    added possibility to use fallback languages for a model, see also updates in README

    @tonydspaniard Let me know if you've questions or feedback regarding this PR, we heavily rely on this in most of our projects.

    It should be BC @cebe Please correct me if not.

    opened by schmunk42 6
  • After delete event for delete translations

    After delete event for delete translations

    Hi @2amigos

    I added new event (EVENT_AFTER_DELETE) which delete translations.

    Could you check it ?

    Do you think it's more safe to leave translations undeleted ?

    opened by mrstroz 6
  • Allow configuration for behavior about deleting translations

    Allow configuration for behavior about deleting translations

    • Allow configuration of afterDelete event
    • implement $restrictDeletion property, to prevent deletion of records with multiple translations

    default behavior does not change, it just go more flexible by allowing more configuration.

    opened by cebe 4
  • Validation error at saving translation remains hidden.

    Validation error at saving translation remains hidden.

    I have the following validation in model IngredientGroupLang:

        public function rules()
        {
            return [
                [['ingredient_group_id', 'language', 'name'], 'required'],
                [['ingredient_group_id'], 'integer'],
                [['language', 'name'], 'string', 'max' => 255],
                [['language', 'name'], 'unique', 'targetAttribute' => ['language', 'name']],
            ];
        }
    

    Uniqueness validation error of IngredientGroupLang at saving of an instance of IngredientGroup remains hidden, what causes that theIngredientGroup will be saved without IngredientGroupLang record, causing an inconsistent database. Even if I overwrite the save method of IngredientGroup to run in transaction, it does not help, because I can not catch the validation error of the translation.

    I think, the problem is, that validation error at saving the translation should raise an exception and the validation error should be bubbled up into the main model instance. The exception could than be catched.

    Do you have any idea? I need a uniqueness validation on the name attribute.

    opened by nagyt234 4
  • Bug when applying limit on query

    Bug when applying limit on query

    I'm using the 2amigos/yii2-translateable-behavior, and when I limit the query set (aka, use per-page parameter), the query is returning all translations of records that match the query criteria.

    Let me try explain this better, imagine the following scenario: I have the table ARTICLE, each article can have one or more translations, stored in ARTICLE_LANG table. I want to return the first 5, if those five only have 1 translation (meaning, original language), I will receive 5 unique records. Now, imagine this twist, where those 5 records, the 2 first records have 2 translations each, what the query will return is 4 records corresponding to the first two, and the fifth will be ARTICLE no.3

    That makes the api return only 3 unique records, instead of the 5 I've requested. @tonydspaniard

    opened by I-NOZex 3
  • Updating doesn't work

    Updating doesn't work

    I've installed this great extension and everything works fine with displaying data already in database. But when i try to update data nothing happens. As far I can see, title property never gets marked as dirty and so never gets updated. I have Article and ArticleTranslation models setup up as by instructions. This is in my activeForm among other fields.

    <?= $form->field($model, 'title')->textInput() ?>

    opened by allesan 3
  • Imrovment: Populate already loaded translations

    Imrovment: Populate already loaded translations

    Example:

            $query = Property::find();
            $query->with([
                'translations'=>function($query){
                    $query->onCondition(['lang' => ['es','de']]);
                },
            ]);
    
    opened by vintik100 3
  • Problem if you not always need translation

    Problem if you not always need translation

    At the moment I use https://github.com/creocoder/yii2-translateable in my media model. In this model I translate the alt and title attribute for images. In some cases I don't need the translation in every view. Therefore it is good that there is no ActiveRecord::EVENT_AFTER_FIND. I think it is better to instancetiate the model with Model::find()->with('translations') if you need the translation.

    under discussion 
    opened by kmergen 2
  • Default translation

    Default translation

    It would be nice if it was possible to define a default translation in case of missing Eg: My site is in 'pt-pt' but I have only translations in 'en-gb'... i think is preferible see the info in another language, than no info at all... Thank you!

    opened by I-NOZex 2
  • translationAttributes requires language attribute

    translationAttributes requires language attribute

    Assumption

    I have same schema as https://github.com/2amigos/yii2-translateable-behavior#preparation

    this is behavior config in tour model

    public function behaviors()
    {
        return [
            'trans' => [ // name it the way you want
                'class' => TranslateableBehavior::className(),
                // in case you named your relation differently, you can setup its relation name attribute
                // 'relation' => 'translations',
                // in case you named the language column differently on your translation schema
                // 'languageField' => 'language',
                'translationAttributes' => [
                    'title', 'description'
                ]
            ],
        ];
    }
    

    Now when i save model in actionCreate as

    $tour_model->lang = 'en';
    

    getting error

    yii\base\UnknownPropertyException: Setting unknown property: \models\Tour::lang in ...

    now i add a lang in behavior as

    'translationAttributes' => [
                    'title', 'description', 'lang'
                ]
    

    and having below line in controller, all works fine

    $tour_model->lang = 'en';
    

    Is this a issue or doc should be updated? I think the doc should be updated that lang att should also be in translationAttributes

    feel free to ask more details if needed

    opened by SOHELAHMED7 1
  • my Has_Many function has a

    my Has_Many function has a "andOnCondition" but its not used

    Hi, I am storing for different accounts separate translations via your model. But that means that I am using this

     return $this->hasMany(Translation::class, ['fk_id' => 'field_id'])
            ->andOnCondition(['customer_id' => Yii::$app->session['active_customer_id']])
            ;
    

    But somehow the query that your behavour fires, does not include the andOn condition. Am I doing something wrong?

    Thanks for any hint....

    gb5256

    opened by gb5256 3
Owner
2amigOS! Consulting Group
Web Application and Mobile Development
2amigOS! Consulting Group
🇸🇮 Slovenian translations - language package for Magento 2

Magento 2 Slovenian translations Slovenian (Slovenia) - sl_SI translations for Magento Community Edition 2.x. Translations follow Slovenian grammar an

Symfony Slovenia 11 Dec 2, 2022
H&O Magento 2 Nederlandse vertalingen / Dutch Translations

Abandoned / End-of-life We're sorry, but this extension is no longer actively maintained. Please use https://github.com/magento-l10n/language-nl_NL in

Reach Digital 28 Mar 5, 2021
Magento commands to find translations that are present in one CSV file but not in another, and to translate CSV dicts with DeepL

Hyvä Themes - Magento translation CSV comparison command hyva-themes/magento2-i18n-csv-diff This module adds the bin/magento i18n:diff-csv and i18n:tr

Hyvä 6 Oct 26, 2022
Laravel-Crowdin Integration - Automate translations uploading/downloading

Laravel-Crowdin Integration Automate uploading/downloading translations Installation Install the package via composer: composer require georgii-web/la

Joris van Willigen 0 May 25, 2022
Small utility library that handles metadata minification and expansion.

composer/metadata-minifier Small utility library that handles metadata minification and expansion.

Composer 134 Dec 26, 2022
A lightweight php class for formatting sql statements. Handles automatic indentation and syntax highlighting.

A lightweight php class for formatting sql statements. Handles automatic indentation and syntax highlighting.

Doctrine 1.4k Dec 29, 2022
A simple mini pos that handles managing product data's and product categories

What is CodeIgniter CodeIgniter is an Application Development Framework - a toolkit - for people who build web sites using PHP. Its goal is to enable

Mahendra Dwi Purwanto 0 Dec 26, 2021
Validated properties in PHP8.1 and above using attribute rules

PHP Validated Properties Add Rule attributes to your model properties to make sure they are valid. Why this package? When validating external data com

null 23 Oct 18, 2022
Magento 2 extension. Strekoza_GoogleCategory. Add Category Attribute (field) - "Google Category"

Magento 2 extension. Strekoza_GoogleCategory Magento 2 extension. Add Category Attribute (field) - "Google Category" This exstension will add Category

Alex S 1 Feb 7, 2022
Clean up your Magento database by removing orphaned, unused and wrongly added attribute, attribute values and settings (for M2).

Magento 2 EAV Cleaner Console Command Purpose of this project is to check for different flaws that can occur due to EAV and provide cleanup functions.

FireGento e. V. - Hackathons 41 Dec 14, 2022
Package to manage Laravel translations locally

Translation Manager For Laravel Easy to use package that helps you with the translation of your Laravel application locally. Features ✅ Check all loca

null 5 Jan 8, 2022
A layout field that forces translations to have the same structure.

Kirby translatedlayout plugin A layout field that forces translations to have anidentical structure to the one of the default language. This is an exp

Daan de Lange 12 Apr 25, 2022
🇸🇮 Slovenian translations - language package for Magento 2

Magento 2 Slovenian translations Slovenian (Slovenia) - sl_SI translations for Magento Community Edition 2.x. Translations follow Slovenian grammar an

Symfony Slovenia 11 Dec 2, 2022
H&O Magento 2 Nederlandse vertalingen / Dutch Translations

Abandoned / End-of-life We're sorry, but this extension is no longer actively maintained. Please use https://github.com/magento-l10n/language-nl_NL in

Reach Digital 28 Mar 5, 2021
Laravel translations checker

Never worry about missing translations again! Use the translations checker.

Lars Wiegers 149 Dec 16, 2022
Magento commands to find translations that are present in one CSV file but not in another, and to translate CSV dicts with DeepL

Hyvä Themes - Magento translation CSV comparison command hyva-themes/magento2-i18n-csv-diff This module adds the bin/magento i18n:diff-csv and i18n:tr

Hyvä 6 Oct 26, 2022
Generates a vue-i18n compatible include file from your Laravel translations

This is fork of martinlindhe/laravel-vue-i18n-generator to give Laravel 8+ support for this excellent package.

Alefe Souza 1 Nov 11, 2021
Filament Translations - Manage your translation with DB and cache

Filament Translations Manage your translation with DB and cache, you can scan your languages tags like trans(), __(), and get the string inside and tr

Fady Mondy 32 Nov 28, 2022
Laravel-Crowdin Integration - Automate translations uploading/downloading

Laravel-Crowdin Integration Automate uploading/downloading translations Installation Install the package via composer: composer require georgii-web/la

Joris van Willigen 0 May 25, 2022
BitView's translations.

BitView Translations Bonjour! You can now create translations for BitView. Please, use en-US.php as a template. Useful Information Date Format Info La

BitView 7 Jun 14, 2023