[bug] storeTranslatableBasicData method not working correctly
Metallizzer opened this issue ยท 9 comments
Hello.
October CMS v3.3.14
RainLab.Translate plugin v2.2.4
When trying to change the translation without changing the value in the model itself, the storeTranslatableBasicData method resets the value of the attribute_data field
Example
// Sets a single translated attribute for a language
$user->setAttributeTranslated('name', 'Jean-Claude', 'fr');
$user->save();
Because the method tries to save only those values that match the dirty values of the parent model
array_intersect_key returns an empty array if the second parameter is empty
We have the same problem but didn't notice it until now and now have multiple, hard to find models with missing translations..
Fixed in v2.2.5
@daftspunk The problem still persists for me. As soon as I enter a attribute for a locale that is not the default but has the same language the value is removed and the field stays empty
This is part of the design. If the locale "message" (language) is the same as the default locale's message, then it isn't stored--the value is still obtained using the fallback logic instead.
To clarify:
// en title is: First Page
$page->setAttributeTranslated('title', 'First Page', 'fr');
If the french title is "First Page", and the english title (default) is also set to "First Page", then it stores as NULL. This way it is considered "untranslated" and if the english message updated, the french message is also updated since they are considered the same source.
@daftspunk Ok, but this causes a problem if I want to know if the model is available in the language, right?
If e.g. the slug is the same in every language and I want to know if the page is available in the respective language. E.g:
Model 1:
Slug DE: test-1
Slug EN: test-1
Model2:
Slug DE: slug-de
Slug EN: slug-en
$locales = array_keys(Locale::listAvailable());
foreach ($locales as $locale) {
$model->translateContext($locale);
if (empty($model->slug) { Do something }
}
I have to set $model->noFallbackLocale($locale);
as well so I don't receive the default value. But if I do this I would never know if it's not translated, or the translation is just the same then the default one. The same applies if I want to know if a model is translated and NOT empty
Is there any way to opt-out this behavior?
Is there any way to opt-out this behavior?
It is a good idea to include a way to opt-out, the original design was to just duplicate everything, but that broke the "fallback" feature (issue #714). In Translate v1, we used the browser to determine if a field was translated so it could be null or it could be duplicated value
I thought something like could work:
// Not reliable
$page->noFallbackLocale()->setAttributeTranslated('title', 'First Page', 'fr');
However, it is only a temporary fix to the state. Next time the model is saved, the comparison check is done and the value is nulled again.
Some other options are a global configuration setting, or a property on the model. In your case, maybe it is best to specify attributes that should not be compared and nulled.
Is your slug an index already? Perhaps we could use this
public $translatable = [
'name',
['slug', 'index' => true]
];
Yes, it's a good idea to enable this behavior on the attribute level, but maybe not with the index
key. We have a model with about 20 fields that are translatable. 2 of these fields (slug
, title
) may contain the same string for each language in about 5% of the cases. Using the index
key would also create an entry in the rainlab_translate_indexes
table, which would be unnecessary.
Maybe it's better to have something like:
public $translatable = [
['title, 'prevent_default' => true],
['slug', 'prevent_default' => true, 'index' => true],
'description',
....
];
What do you think?
Looks good. This has been added in ac38a18 and docs updated (below).
Fallback Attribute Values
By default, untranslated attributes will fall back to the default locale. This behavior can be disabled by calling the noFallbackLocale
method for reading the value.
$user = User::first();
$user->noFallbackLocale()->lang('fr');
// Returns NULL if there is no French translation
$user->name;
When writing the value, the fallback value is determined when the translated value matches the default value. In these cases, the translated value is considered untranslated and not stored.
Locale | Attribute | Value | Is Stored |
---|---|---|---|
en | title | Hello World | Yes (Default) |
fr | title | Hello World | No |
de | title | Hallo Welt | Yes |
For example, if the en
default locale stores the message as "Hello World" and the fr
locale value is also "Hello World", then the fr
value is not stored. The fr
value is accessed using the fallback value taken from en
.
You may disable this behavior by passing the $transatable
attribute value as an array. The first value is the attribute name, the other values represent options, in this case setting the option fallback
to false
.
public $translatable = [
['title', 'fallback' => false]
];
This above definition will force the title
attribute value to be duplicated across all locales.
Released in v2.2.6