There's a common and pretty annoying case in localizing web apps, which is when you need tags inside the translated text. For instance, I have this original needing translation:
<p>
<router-link :to="{ name: 'login' }">Sign in</router-link>
or
<router-link :to="{ name: 'register' }">sign up</router-link>
to add comments on this article.
</p>
The correct would be to have the phrase translated as a whole, and you clearly do not want to risk that code being changed by your translators, besides the security risk of having translated text executed as code. Your project already has a very good solution using the i18n
component, in which case I could have a fluent resource like:
sign-in = Sign in
sign-up = sign up
sign-in-up-to-add-comments = {$signIn} or {$signUp} to add comments on this article.
And the code:
<i18n path="sign-in-up-to-add-comments" tag="p">
<template #signIn>
<router-link :to="{ name: 'login' }">{{ $t('sign-in') }}</router-link>
</template>
<template #signUp>
<router-link :to="{ name: 'register' }">{{ $t('sign-up') }}</router-link>
</template>
</i18n>
That goes very far and almost solves the problem, but there is a big issue: the "Sign in" and "sign up" texts are in the context of the sentence and could potentially have alternative casing depending on the language, for instance, it could be that in some language there's a first word that is capitalized and the "Sign in" would then not be. Currently the only way to solve this would be to create new keys for the words, like sign-in-up-to-add-comments-sign-in
, but that is not ideal considering that the fluent language already has tools to specifically deal with things like that.
The ideal would be to have a fluent resource like:
# The two parameters will be replaced with links and each link
# will use the .sign*Label as its text
# The reason for the labels to be camel case will become clear shortly...
sign-in-up-to-add-comments =
{$signInLink} or {$signUpLink} to add comments on this article.
.signInLabel = Sign in
.signUpLabel = sign up
You can already use that fluent feature in some cases by making use of the $ta
method and the v-t
directive, and it would be great if that directive idea could be generalized for the component case as well. Fortunately vue.js already has a way to deal with cases like this in the form of Slot Props.
I propose that the i18n
component also use the formatAttrs
method and pass the object to the slots as slot props. In which case, using the same fluent resource mentioned above, we could have a code like this:
<i18n path="sign-in-up-to-add-comments" tag="p">
<template #signInLink="{ signInLabel }"> <!-- < destructuring the slot props could be used if the key is camel-case -->
<router-link :to="{ name: 'login' }">{{ signInLabel }}</router-link>
</template>
<template #signUpLink="{ signUpLabel }">
<router-link :to="{ name: 'register' }">{{ signUpLabel }}</router-link>
</template>
</i18n>
It's actually a pretty trivial change and I already implemented it. I'll send a pull request shortly. To avoid affecting existing code and making that intent explicit, I added a new prop to the component called use-ta
that controls if the formatAttrs
will be used.
BTW, great project, thank you for creating it! I'm writing an article coalescing my ideas of a scalable workflow for properly localizing web/vue applications and this project is one of its cornerstones. I'll probably have additional feature/pull requests in the near future.
Cheers!