THIS DOCUMENT IS OUT OF DATE
We want a multilingual site to which a client like UFC can add his own translations.
Translations live in these places:
- site translations default translations for the nuxt site: experiences//i18n-messages-default.json
- experience translations, default translations for each experience, for example in packages/packages/experiences/google/src/index.ts
- custom translations loaded from an url defined in the config, for example at experiences/static/i18n-messages-example.json
site and experience translations affect different parts of the website, they should not have any common keys. custom translations can override any translation of the other two.
Everything is done using vue-i18n. The override relationships between messages are implemented by modifying vue-i18n's global dictionary.
In addition to wanting a french version of experiences, UFC wants to be able to edit the introduction text of each experience from their a cms. This solution relies on translations being loaded in a specific json format from a url.
In our first discussion with UFC, we imagined that the cms would produce html that could be included in an iframe. I hope it's possible to have it as an html snippet rather than a whole html page like iframes normally work with. It's possible that they imagined having a different url for the introduction text for each experience. If they absolutely need to do that, we could implement a function that converts their format to ours.
Define text snippets in the vue-i18n json format. A custom translation file like i18n-messages-example.json looks somewhat like this:
{
"en": { ... },
"fr": {
// This overrides a site translation
// which stays the same for every experience
"experience": {
"intro": {
"title": "Analysez vos données privées"
}
},
// This overrides experience translations,
// you'll only see it in the google experience
"experiences": {
"google": {
"intro": {
"dataPortalHtml": "Cliquez <a href=\"https://takeout.google.com/settings/takeout\">ici</a>",
"dataPortalMessage": "<strong>Important:</strong> pour que l'expérience"
}
}
}
}
}
Site translations are used by calling functions from vue-i18n like $t() and $te()
<VCardTitle class="text-h5 font-weight-bold justify-center mb-3">
{{ $t('experience.intro.title') }}
</VCardTitle>
For experience translations, keys need to be converted to the global vue-i18n namespace using a function k() implemented by us.
<p
v-else-if="$te(k('dataPortalHtml'))"
class="body-1"
v-html="$t(k('dataPortalHtml'))"
/>
These examples come from UnitIntroduction.vue (The only place where they're implemented in this proof of concept)
The default messages are loaded at build time by nuxt.config.js
The custom messages are loaded by the browser, in experiences/layouts/default.vue
The experience message are loaded by components like TheDataExperience.vue and it's children. The proof of concept is implemented in UnitIntroduction.vue
Every component needs two methods store and access its messages in the vue-i18n global dictionary
/** Convert a local, experience-specific key to a vue-i18n key. */
// Implement this on each component with experience specific i18n
i18nMakeGlobalKey(localKey) {
return `experiences.${this.experienceName()}.intro.${localKey}`
},
/**
* Wrap a local, experience-specific, key-value object into a format
* that can be merged into vue-18n's global dictionary.
*/
i18nWrapInGlobalNamespace(messages) {
return {
experiences: { [this.experienceName()]: { info: messages } }
}
},
These two methods are used at component creation, when the default messages taken from the packaged experience, and then merged into the vue-i18n global dictionary:
created() {
const experience = this.$store.getters.experience(this.$route)
// TODO come up with a way to specify i18n messages in packaged experiences
// const locale = this.$i18n.locale
const defaultMessagesForLocale = pick(experience, [
'title',
'dataPortal',
'dataPortalHtml',
'dataPortalMessage'
])
this.i18nMergeComponentMessages(
defaultMessagesForLocale,
k => this.i18nMakeGlobalKey(k),
m => this.i18nWrapInGlobalNamespace(m),
this.$i18n
)
},
note: i18nMergeComponentMessages is meant to be used by all components with experience translations. It should be moved to a central place.
The config file dev.json has an example of a locale set to french and an url to load custom translations.
The custom translation example configures the introduction of the google experience
Here's a direct link to the google experience running locally. http://localhost:3000/experience/google#load-data
- Define a format to store multiligual messages in packaged experiences, for example in a messages.json file that uses the same format as vue-i18n. We could keep it backwards compatible with the current format, but maybe that's not necessary.
- Replace every single text in the app with a message configured in vue-i18n
- Translate all the texts to french
- Talk with UFC to agree on the integration with their CMS
- BUG: the links in the menu are broken, probably because nuxt-i18n affects the behavior of the router nuxt-link. Hopefully this can be fixed by configuring nuxt-i18n in some way