6

I'm creating a Vue web component using vue-cli 4.5.13 and the --target wc option. I also need the component to use the vue-i18n-next plugin (last version for vue 3 projects), which requires some options to be passed to the main Vue instance, but now there is no main instance since the entrypoint is the vue component itself, so i18n has to be instantiated within the component.

I found this question for vue2: How can I use vue-i18n in a Vue web component? and it works for vue2, but instantiating the i18n object in the component options is not valid for vue3 (this.$i18n stays undefined). So this is not working:

export default {
  name: "custom-compo",
  i18n: i18n,
  components: { ChildComponent }
  data, methods...
};

The solution should work both if exporting the web component (and using it on a standard page) and also if npm run serve ing a standard app with this component inside (but again instantiating i18n in the compo as in answer to that question, not in main.js)

I am not asking how to setup vue-i18n within a normal vue project, but how to inisitalise i18n within a component that's gonna be built or exported as a custom-element or web-component, using the vue-cli, with vue 3.

I am not using composition api and setup(), but old options api. Just upgraded to vue3 package and changed deprecated syntax as per docs.

tony19
  • 125,647
  • 18
  • 229
  • 307
DieSeL
  • 131
  • 2
  • 8
  • Does this help you? https://stackoverflow.com/questions/69103550/vue-i18n-alternative-for-this-i18n-using-the-composition-api – anatolhiman Sep 27 '21 at 22:18
  • Thanks @anatolhiman, close! but the referred question implements composition api and I am sticking to options api. – DieSeL Oct 13 '21 at 19:41
  • Have you found anything on this? – leonheess Feb 08 '23 at 14:40
  • Anything written in Options API can be written in Composition API and vice-versa. The only thing changed is how you group code. In options you need to group component members by type (computed, data, methods) whereas in Composition you can group them by any criteria you want, inside setup. – tao May 19 '23 at 10:13

2 Answers2

0

Step 1: Install vue-i18n and dependency npm package using the commands below,

npm install vue-i18n@next
npm install --save-dev @intlify/vue-i18n-loader

Step 2: Create i18n as seperate i18n.js file inside src folder

import { createI18n } from 'vue-i18n'


function loadLocaleMessages () {
  const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
  const messages = {}
  locales.keys().forEach(key => {
    const matched = key.match(/([A-Za-z0-9-_]+)\./i)
    if (matched && matched.length > 1) {
      const locale = matched[1]
      messages[locale] = locales(key).default
    }
  })
  return messages
}

export default createI18n({
  locale: process.env.VUE_APP_I18N_LOCALE || 'en',
  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: loadLocaleMessages()
})

Step 3: Import i18n in main.js

import i18n from './i18n'

Step 4: Inside main.js add the i18n, router, store reference to createApp

import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import i18n from './i18n'
const app = createApp(App).use(i18n).use(store).use(router)
app.mount('#app')

Step 5: Design the language dropdown menu

<li class="nav-item dropdown" ref="dropdown">
  <button class="nav-link" data-toggle="dropdown" @click="toggleDropdown">
    <i class="flag-icon " :class="selectedLanguage === 'en' ? 'flag-icon-us': 'flag-icon-es'"></i>
  </button>
  <div class="dropdown-menu dropdown-menu-right p-0" :class="{'show':isDropdownOpened}">
    <a href="#" class="dropdown-item" :class="selectedLanguage === 'en' ? 'active': ''" @click.prevent="changeLanguage('en')">
      <i class="flag-icon flag-icon-us mr-2"></i> English
    </a>
    <a href="#" class="dropdown-item" :class="selectedLanguage === 'es' ? 'active': ''" @click.prevent="changeLanguage('es')">
      <i class="flag-icon flag-icon-es mr-2"></i> Spanish
    </a>
  </div>
</li>

Language Dropdown

Step 6: Add the changeLanguage method inside the component

changeLanguage (locale) {
  this.$i18n.locale = locale
  this.selectedLanguage = locale
  this.isDropdownOpened = false
}

Step 7: Create locales folder inside src folder

Step 8: Create en.json and es.json two seperate files

Step 9: Inside en.json file likes below

{
  "common.login": "Login",
  "common.signout": "Sign Out",
  "common.switchuser": "Switch User",
  "common.profile": "Profile",
  "common.submit": "Submit"
}

Step 10: Inside es.json file likes below

{
   "common.login": "Acceso",
   "common.signout": "Desconectar",
   "common.switchuser": "Cambiar de usuario",
   "common.profile": "Perfil",
   "common.submit": "Enviar"

}

Jebasuthan
  • 5,538
  • 6
  • 35
  • 55
  • 7
    Please read my question. This is not what I am asking. I am not asking how to setup vue-i18n within a normal vue project, but how to inisitalise i18n within a component that's gonna be built or exported as a custom-element or web-component, using the vue-cli. – DieSeL Aug 19 '21 at 07:31
0

As to my experience, to add plugins to the web-component requires special injection of the plugin. You can check the implementation in this plugin https://www.npmjs.com/package/vue-web-component-wrapper

Where I add the plugin into a normal vue instance and then add the plugins with Object.assign to web-component instance

export const defineCustomElement = ({
    rootComponent,
    plugins,
    cssFrameworkStyles,
    VueDefineCustomElement,
    h,
    createApp, 
    getCurrentInstance
}) => VueDefineCustomElement({
        styles: [cssFrameworkStyles],
        render: () => h(rootComponent),
        props: { ...rootComponent.props },
        setup() {
            const app = createApp();
            app.component('main', rootComponent)
            app.mixin({
                mounted() {
                    const insertStyles = (styles) => {
                        if (styles?.length) {
                            this.__style = document.createElement('style');
                            this.__style.innerText = styles.join().replace(/\n/g, '');
                            nearestElement(this.$el).prepend(this.__style);
                        }
                    };

                    insertStyles(this.$?.type.styles);
                    if (this.$options.components) {
                        for (const comp of Object.values(this.$options.components)) {
                            insertStyles(comp.styles);
                        }
                    }
                },
                unmounted() {
                    this.__style?.remove();
                },
            });
           
            app.use(plugins);
            const inst = getCurrentInstance();
            Object.assign(inst.appContext, app._context)
            Object.assign(inst.provides, app._context.provides)
        },
    });
;
EranGrin
  • 258
  • 4
  • 9