When working with vue-i18n
there is an issue with dynamic language changes.
For example when using input rules on v-text-field
. Within the rule one can call i18n.t('...')
to translate error-messages. The problem is, that on language-change the messages don't get updated to the new locale unless the validation is triggered again.
There is a similar issue on the usage of hints and labels. Avoiding the $t
for performance-reasons leads to very bloated code and is not always possible (see example).
<v-text-field
:rules="[val => !!val || $t('messages.required')]"
:hint="$t('messages.hint')"
>
<template #label>
<span v-t="'messages.label'"></span>
</template>
</v-text-field>
Just assume the keys messages.required
, messages.label
and messages.hint
exist.
The label can be displayed with the better performing v-t
directive and the 'label' slot, for the hint $t
has to be used even if you try to avoid it.
It would be nice to have a property like use-i18n
or similar. If set to true, the component would take the strings provided for hint
, label
and as return of the rules and try to use it for the path on i18n translation.
This behavior could also be extended to the item-text
values of v-select
components.
To have better control about where this behavior is applied or not the property could accept an object like { label: true, hint: false, rules: true}
.
This feature is intended to input components but could be used on other components too.
A similar solution (use-i18n) could also work for data-table headers. Currently it's not easy to have i18n table headers.
A similar solution (use-i18n) could also work for data-table headers. Currently it's not easy to have i18n table headers.
I found a solution for Table headers, still,it needs Computed Properties which can lead to bloated Code when used to frequently.
{
// ...
computed: {
cTableHeaders () {
const locale = this.$i18n.locale
const t = this.$t.bind(this)
return [
{
text: t(`table.attribute`, locale),
value: 'name'
}, {
text: t(`table.type`, locale),
value: 'configuration.type'
}, {
text: t(`table.required`, locale),
value: 'required'
}, {
text: t(`table.searchable`, locale),
value: 'searchable'
}, {
text: t(`table.order`, locale),
value: 'actions'
}
]
}
}
}
// ...
}
<v-data-table
:headers="cTableHeaders"
></v-data-table>
Just imagine the i18n-keys refer to some useful values.
This way even with locale-change on run-time the Headers get updated.
My current solution is to use header slots, one for each column.
(this example uses plain Vuetify i18n)
<template v-slot:header.firstName="{ header }">
{{$vuetify.lang.t('$vuetify.' + header.text)}}
</template>
<template v-slot:header.lastName="{ header }">
{{$vuetify.lang.t('$vuetify.' + header.text)}}
</template>
<template v-slot:header.address="{ header }">
{{$vuetify.lang.t('$vuetify.' + header.text)}}
</template>
Both variants use too much bolierplate. I would be nice to have better i18n support built-in.
bit less boilerplate
<template v-for="header in headers" v-slot:[`header.${header.value}`]="{ header }">
{{ $vuetify.lang.t(`$vuetify.${header.text}`) }}
</template>
Nice! I didn't know you could create dynamic slots like that. Thanks.
As detailed here, I'm doing something similar and running into this same problem with translating bound label values. Here's a standard setup:
<v-layout row>
<v-flex xs6>
<v-text-field
v-bind="fields.userId"
v-model="userModel.userId"
:disabled="!canSave"
/>
</v-flex>
<v-flex xs6>
<v-text-field v-bind="fields.id" v-model="userModel.id" disabled />
</v-flex>
</v-layout>
...
import { fields, buttons, csvFields } from '@/components/config/users';
...
data: () => ({
fields,
...
})
and here is fields.js
:
import RuleGenerator from '@/utils/RuleGenerator';
import i18n from '@/i18n';
const fields = {
userId: {
label: i18n.t('users.generalInfo.userId'),
// Set label to 'Email' because the username is required to be an email address.
rules: new RuleGenerator('Email')
.setRequired()
.setMin(1)
.setMax(255)
// eslint-disable-next-line no-useless-escape
.setRegEx(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)
.getRules(),
hint: 'Must be a valid email address',
counter: '255',
},
id: {
label: i18n.t('users.generalInfo.id'),
},
};
export default fields;
When I change the value of this.$root.$i18n.locale
using a click handler:
setLocale(locale) {
this.$root.$i18n.locale = locale;
},
the label values don't change without a refresh. However, if I put the label in the field definition directly:
<v-text-field
v-bind="fields.userId"
v-model="userModel.userId"
:disabled="!canSave"
:label="$t('users.generalInfo.userId')"
/>
changing the locale dynamically changes the string as expected.
I think this is the same issue as the original poster's problem. Judging from the label applied to this issue, the capability to handle this would be an added feature. I suppose I could do computed properties (as is shown above with table headers), but this pattern is all over a large app, so it would require some serious re-working. Given my structure, is there an easier way to get around this, at least until this gets added to Vuetify?
Thanks to @jacekkarczmarczyk for the dynamic slots, is just beautiful, and to @sense-it-gmbh for the computed work around.
I've write this codepen for the lazys like me:
https://codepen.io/TadeoArmenta/pen/pojOxvo
I came here because when use templated headers and i18n, you can make them work without the computed hack of @sense-it-gmbh, but breaks the mobile view who jumps to the object "text" value
Is there any known good workaround for the issue originally described in the report (changing language doesn't change language of validation error messages when using i18n.t('...') within the rules)?
Is there any known good workaround for the issue originally described in the report (changing language doesn't change language of validation error messages when using i18n.t('...') within the rules)?
@albert0815 I have had a struggle with this for a long time now and i dont think there is a perfect solution for the validation rules. The problem here is that the message is taken directly from the rule-result itself, this way the i18n.t
doesn't get recomputed on locale-change (how could it know that it should). Anyway, users usually don't change the language while filling in a form, they usually do after visiting the app or changing the route so i dont see a very big problem.
If you really need this you could probably watch i18n.locale
and upon change you could try to trigger the form validation again. But I have the feeling that this would mean some overhead that is most probably not worth the time.
@ EVERYONE ELSE: If you ever run into the problem that you want to export a localized rule-set from a library shared across different projects you should try encapsulating it in a vue-plugin. This way you can access the i18n
object of the project importing the rules instead of having to struggle with your own i18n
instance and how to sync it to the one of the app.
Is there any known good workaround for the issue originally described in the report (changing language doesn't change language of validation error messages when using i18n.t('...') within the rules)?
v-text-field
and other v-input elements all have a scoped slot named message
, so you can use the same way mentioned above to solve the problem, but need some tricks.
The tricks are that you should pass a i18n key rather than $t(...)
to the rule function, like this :rules="[ v => !!v || 'an-i18n-key' ]"
.
And then you should call $t('an-i18n-key')
in the slot template to show the translated messages.
<v-text-field :rules="[ v => !!v || 'an-i18n-key' ]">
<template #message="{ message }">
{{ $t(message) }}
</template>
</v-text-field>
My English is bad, I hope you can get what I mean ;D
@johnleider and team: For Vuetify 3, please consider making i18n a first-class citizen (like i11y is today). Internationalization in Vuetify 2.x requires a lot of boilerplate and IMO is not flexible enough for enterprise apps (my field). I don't have the solution here and now. I just want to hint that v3 is an opportunity to make i18n a killer feature. For example maybe all label
properties in all components, and rules
could delegate to a (pluggable) message lookup service of some kind. The default impl could just return the key. I'm open for discussion on the subject at any time.
Most helpful comment
bit less boilerplate