My problem is simple, yet I can\'t figure out how to solve it.
My website is multilanguage. I want the user to be able to add an article in multiple language if he w
The fact that the input for the default language is being stored in the translation table in case the default locale has been changed, seems to be the expected behavior, just like when reading data where it will retrieve the data with respect to the current locale, the same applies when saving data.
Cookbook > Database Access & ORM > Behaviours > Translate > Saving in Another Language
Changing the locale to the default is a workaround, but it might be a little too invasive, as it will interfer with any code that uses that value to check the current locale. It's better to directly set the desired locale on the table
$Ingredients->locale(I18n::defaultLocale());
or, which is the least invasive option, on the main entity instead
$ingredient->_locale = I18n::defaultLocale();
Also the former is what the linked docs sesction is describing, but not actually showing, that needs to be fixed.
While I can see why the form helper, respectively the entity context, picks up validation rules for the "wrong" fields, ie xyz.name
fields pick up those for the name
field, I can't tell whether this is how it is ment to work.
Since it wouldn't pick up nested errors, I guess this is the expected behavior, but I'm not sure, so I'd suggest to create an issue over at GitHub for clarification. In any case, there are various ways to work around this, for example by renaming the fields, or by setting the required
option to false
.
echo $this->Form->input('locales.fr_CA.name', [
// ...
'required' => false
]);
In your example this is pretty much just a frontend issue, as the fields are not going to be actually validated on the server side.
Another option would be to use a custom translation table class, with validation specific to translations, that actually apply to the used fields, however this is probably not that advisable unless you actually want to apply any validation at all.
For the sake of completion, let's cover validation/application rules too.
In order to actually apply validation and/or application rules, and have them recognized in forms, you'll have use a custom translation table class that holds the rules, and you must use the actual property name that the translate behavior uses for the hasMany
associated translation table, which is _i18n
.
Here's an example.
src/Model/Table/IngredientsI18nTable.php
namespace App\Model\Table;
use Cake\Datasource\EntityInterface;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class IngredientsI18nTable extends Table
{
public function initialize(array $config) {
$this->entityClass('Ingredient');
$this->table('i18n');
$this->displayField('id');
$this->primaryKey('id');
}
public function validationDefault(Validator $validator) {
$validator
->allowEmpty('name')
->add('name', 'valid', [
'rule' => function ($value, $context) {
return false;
}
]);
return $validator;
}
public function buildRules(RulesChecker $rules)
{
$rules->add(
function (EntityInterface $entity, $options) {
return false;
},
'i18nName',
[
'errorField' => 'name'
]
);
return $rules;
}
}
IngredientsTable
public function initialize(array $config) {
// ...
$this->addBehavior('Translate', [
// ...
'translationTable' => 'IngredientsI18n'
]);
}
View template
echo $this->Form->hidden('_i18n.0.locale', ['value' => 'fr_FR']);
echo $this->Form->input('_i18n.0.name');
echo $this->Form->hidden('_i18n.1.locale', ['value' => 'da_DK']);
echo $this->Form->input('_i18n.1.name');
// ...
Now the fields will pick up the correct validator, and thus are not being marked as required. Also validation will be applied when creating/patching entities, and finally application rules are being applied too. However I can't guarantee that this doesn't have any side effects, as the Translate behavior internally doesn't seem to account for the situation that the _i18n
property has been set externally!
Also you'll still have to set the translations on the entity using translations()
in order for the translations to be saved correctly!
foreach ($this->request->data['_i18n'] as $translation) {
$ingredient->translation($translation['locale'])->set('name', $translation['name']);
}