Translate Behavior in Cakephp 5.x

Hi all,

I have been experimenting with the cakephp Translate Behaviour. Reading the docs, I liked the concept, but trying out I felt some discomforts. So I would like to share so that it can be improved.

The current view is assuming all languages contents are translating at the same time.

  1. Call inside the template
<!-- File: templates/Articles/add.php -->

<h1>Add Article : ('title').</h1>
<?php
echo $this->Form->create($article);
// Hard code the user for now.
echo $this->Form->control('user_id', ['type' => 'hidden', 'value' => 1]);
echo $this->Form->control('title');
echo $this->Form->control('body', ['rows' => '3']);
/*
<fieldset>
    <legend>English</legend>
    <?= $this->Form->control('_translations.en_GB.title'); ?>
    <?= $this->Form->control('_translations.en_GB.body', ['rows' => '3']); ?>
</fieldset>
*/
?>
<fieldset>
    <legend>Spanish</legend>
    <?= $this->Form->control('_translations.es_AR.title'); ?>
    <?= $this->Form->control('_translations.es_AR.body'); ?>
</fieldset>
<?php
echo $this->Form->control('tag_string', ['type' => 'text']);
echo $this->Form->button(__('Save Article'));
echo $this->Form->end();
?>

If you have closely looked at the above template, you can see the default locale field is using $this->Form->control('title') for rendering and not as $this->Form->control('_translations.en_GB.title') .

If we go with $this->Form->control('_translations.en_GB.title') path, the title field inside the articles table will be empty. The default locale en_GB goes to the articles_translations table.

  1. Validation

According to the docs we can add validation as

$this->addBehavior('Translate', [
    'strategyClass' => ShadowTableStrategy::class,
    'fields' => ['title', 'body'],
    'validator' => 'translated'
]);

so a validator as

public function validationTranslated(Validator $validator): Validator
{
    $validator
        ->notEmptyString('title')
        ->minLength('title', 10)
        ->maxLength('title', 255)

        ->notEmptyString('body')
        ->minLength('body', 10);

    return $validator;
}

But as we are using the $this->Form->control('title') for the defaultLocale. So to validate the default fields we need to add another default validator.

public function validationDefault(Validator $validator): Validator
{
    $validator
        ->notEmptyString('title')
        ->minLength('title', 10)
        ->maxLength('title', 255)

        ->notEmptyString('body')
        ->minLength('body', 10);

    return $validator;
}

I think that is an extra overhead.

  1. Accessing the value
$lang = I18n::setLocale('es_AR');

// Update retrieving tags with contain()
$article = $this->Articles
    ->find('translations')
    ->where(['slug' => $slug])

    ->contain('Tags')
    ->firstOrFail();

Suppose you have the article object now you can access the value as

  1. $article->title : Which will provide the value from the current locale.
  2. $article->translation('es_AR')->title); get the value of the locale.

The above two works. But lets assume your default locale is en_GB and your current locale set is different. Now in-order to get the default locale, calling $article->translation('en_GB')->title); is not giving you value.

Not sure if this is a bug or this is the default behavior.

All the source code for the experiment is located at GitHub - harikt/cakephp-experiments if anyone want to test this.

So I digged a bit into the code. 3rd case is as expected as in code.

As we can alter this behavior via a different trait, that seems not a big deal.

#2 is also a non issue. Using a validator named translated is not mandatory, it’s just an example. You can just use the default validator by setting 'validator' => 'default' for the behavior.