diff --git a/app/javascript/apps/index.js b/app/javascript/apps/index.js
index 8f8cfd4910..32e895fac7 100644
--- a/app/javascript/apps/index.js
+++ b/app/javascript/apps/index.js
@@ -1,18 +1,36 @@
import { createApp } from 'vue';
+import { createI18n } from 'vue-i18n';
import SsoMappingApp from './sso-mapping/SsoMappingApp.vue';
import MediaPickerApp from './media-picker/MediaPickerApp.vue';
import TimeSlotsApp from './time-slots/TimeSlotsApp.vue';
import BlocksEditorApp from './blocks-editor/BlocksEditorApp.vue';
-if (document.getElementById('sso-mapping-app')) {
- createApp(SsoMappingApp).mount('#sso-mapping-app');
-}
-if (document.getElementById('media-picker-app')) {
- createApp(MediaPickerApp).mount('#media-picker-app');
-}
-if (document.getElementById('time-slots-app')) {
- createApp(TimeSlotsApp).mount('#time-slots-app');
-}
-if (document.getElementById('blocks-editor-app')) {
- createApp(BlocksEditorApp).mount('#blocks-editor-app');
+// Messages are precompiled at boot into public/vue/
.json (cf.
+// config/initializers/vue_i18n.rb) and fetched once as a cacheable static file,
+// then shared by every app through a single vue-i18n instance.
+async function boot() {
+ const locale = document.documentElement.lang || 'fr';
+ const messages = await fetch(`/vue/${locale}.json`)
+ .then((res) => res.json())
+ .catch(() => ({}));
+ const i18n = createI18n({
+ legacy: false,
+ globalInjection: true,
+ locale,
+ fallbackLocale: 'fr',
+ messages: { [locale]: messages },
+ });
+
+ const mount = (App, selector) => {
+ if (document.querySelector(selector)) {
+ createApp(App).use(i18n).mount(selector);
+ }
+ };
+
+ mount(SsoMappingApp, '#sso-mapping-app');
+ mount(MediaPickerApp, '#media-picker-app');
+ mount(TimeSlotsApp, '#time-slots-app');
+ mount(BlocksEditorApp, '#blocks-editor-app');
}
+
+boot();
diff --git a/app/javascript/apps/media-picker/MediaPickerApp.vue b/app/javascript/apps/media-picker/MediaPickerApp.vue
index 4c8bbf781f..9a24d8cda7 100644
--- a/app/javascript/apps/media-picker/MediaPickerApp.vue
+++ b/app/javascript/apps/media-picker/MediaPickerApp.vue
@@ -16,7 +16,6 @@ export default {
data () {
return {
current: {},
- i18n: {},
}
},
methods: {
@@ -57,7 +56,6 @@ export default {
},
beforeMount() {
this.dataset = document.getElementById('media-picker-app').dataset
- this.i18n = JSON.parse(this.dataset.i18n);
this.summernoteLang = this.dataset.summernoteLang;
this.current = JSON.parse(this.dataset.current);
},
@@ -70,7 +68,7 @@ export default {
diff --git a/app/javascript/apps/media-picker/components/Cloud.vue b/app/javascript/apps/media-picker/components/Cloud.vue
index f4c765721c..1741654fa8 100644
--- a/app/javascript/apps/media-picker/components/Cloud.vue
+++ b/app/javascript/apps/media-picker/components/Cloud.vue
@@ -27,7 +27,6 @@ export default {
total_pages: 0,
}
},
- i18n: {},
}
},
methods: {
@@ -93,7 +92,6 @@ export default {
const dataset = document.getElementById('media-picker-app').dataset;
this.lang = dataset.lang;
this.query = JSON.parse(dataset.current).about.name;
- this.i18n = JSON.parse(dataset.i18n).mediaPicker.cloud;
this.settings = JSON.parse(dataset.cloud);
},
};
@@ -105,28 +103,28 @@ export default {
class="btn btn-sm ms-n2"
@click="open()">
- {{ i18n.button }}
+ {{ $t('mediaPicker.cloud.button') }}