Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot pass vue-i18n object into web-component #216

Open
rostamiani opened this issue Jul 5, 2020 · 6 comments
Open

Cannot pass vue-i18n object into web-component #216

rostamiani opened this issue Jul 5, 2020 · 6 comments

Comments

@rostamiani
Copy link

I have a component that uses vue-i18n and works as a normal vue component.

When creating web component I had TypeError: this is undefined error with typescript, then created the component with this approach:

const layout = new ModuleLayout().$options;
Vue.customElement('my-layout', layout);

And this is my i18n object:

import messages from '@/locales/i18n.json';

export const i18n = new VueI18n({
  locale: process.env.VUE_APP_I18N_LOCALE || 'en',
  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: messages
})

For adding i18n I tried these approaches but none works:

const layout = new ModuleLayout({ i18n }).$options;

Vue.customElement('my-layout', layout);
const layout = new ModuleLayout().$options;
layout.i18n = i18n;

Vue.customElement('my-layout', layout);

In both ways I have these errors:

TypeError: "this.$i18n is undefined"
TypeError: "i18n is undefined"
@karol-f karol-f self-assigned this Jul 5, 2020
@LucasBerger
Copy link

Hey,

don't know if that helps but a coworker of mine added i18next with a mixin that he then installed in Vue:

i18next-mixin.ts:

import _Vue from 'vue'
import i18next from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'

import localesDE from '@/locales/de.json'
import localesEN from '@/locales/en.json'

declare global {
  interface Window {
    webpackHotUpdate: Function;
  }
}

interface TranslationOptions {
  defaultValue: string | null;
}

function install (Vue: typeof _Vue): void {
  i18next
    .use(LanguageDetector)
    .init({
      debug: typeof window.webpackHotUpdate !== 'undefined',
      defaultNS: 'common',
      whitelist: ['de', 'en'],
      fallbackLng: 'de',
      returnNull: true,
      resources: {
        en: { common: localesEN },
        de: { common: localesDE }
      },
      detection: {
        order: ['querystring', 'cookie', 'navigator'],
        lookupQuerystring: 'language',
        lookupCookie: 'language',
        checkWhitelist: true
      }
    })

  Vue.prototype.i18next = Vue.observable({ language: 'none' })
  Vue.prototype.$t = function (namespace: string, options: TranslationOptions | undefined) {
    // Important line, keep it else Vue will not call this function when the language changes
    // eslint-disable-next-line
    const lng = Vue.prototype.i18next.language

    if (!options) {
      options = { defaultValue: (typeof window.webpackHotUpdate !== 'undefined' ? namespace : '') }
    } else {
      options.defaultValue = (typeof window.webpackHotUpdate !== 'undefined' ? namespace : '')
    }

    return i18next.t(namespace, options)
  }
}

export default install

then you install it to Vue using:

import I18Nmixin from './mixins/i18next-mixin'

Vue.use(I18Nmixin);

and in the Element you use the $t() method, but I think you can change it by changing it in this line:

Vue.prototype .$t = function (namespace: string, options: TranslationOptions | undefined) {

I hope this helps at least a bit,

Regards Lucas

@karol-f
Copy link
Owner

karol-f commented Jul 6, 2020

Hi, all seems to work for me - https://codesandbox.io/s/vue-template-o1fsc?file=/src/main.js

@karol-f karol-f closed this as completed Jul 6, 2020
@rostamiani
Copy link
Author

rostamiani commented Jul 7, 2020

Hi, all seems to work for me - https://codesandbox.io/s/vue-template-o1fsc?file=/src/main.js

It's working because you are not using Typescript + Class based components. Using typescript I you have to create a new instance of Test component in the main.ts file

Sadly I couldn't start a typescript sandbox to reproduce the error...
But this is my code:

import ModuleLayout from '@/components/modules/layout/ModuleLayout.vue';
import VueCustomElement from 'vue-custom-element';

Vue.use(VueCustomElement);
Vue.customElement('skyroom-layout', ModuleLayout); // line 32

And the error:

Uncaught TypeError: this is undefined
    VueComponent VueJS
    connectedCallback vue-custom-element.esm.js:484
    connectedCallback vue-custom-element.esm.js:57
    define vue-custom-element.esm.js:70
    registerCustomElement vue-custom-element.esm.js:104
    vueCustomElement vue-custom-element.esm.js:477
    ts main.ts:32
    Webpack 7
vue.runtime.esm.js:5154

And this code has no errors but without i18n:

import ModuleLayout from '@/components/modules/layout/ModuleLayout.vue';
import VueCustomElement from 'vue-custom-element';

Vue.use(VueCustomElement);

const layout = new ModuleLayout({ i18n }).$options;
Vue.customElement('skyroom-layout', layout);

@karol-f
Copy link
Owner

karol-f commented Jul 7, 2020

Hi, it seems to work like this. Please let me know if it will work for You - https://codesandbox.io/s/vue-template-72o5z?file=/src/main.js

import Vue from "vue";
import vueCustomElement from "vue-custom-element";
import VueI18n from "vue-i18n";
import Test from "./components/Test.vue";

Vue.use(vueCustomElement);
Vue.use(VueI18n);

Vue.config.productionTip = false;

const i18n = new VueI18n({
  locale: "en",
  messages: {
    en: {
      message: {
        hello: "hello world"
      }
    }
  }
});
Test.prototype.constructor.options.i18n = i18n;

Vue.customElement("test-component", Test.prototype.constructor.options);

@rostamiani
Copy link
Author

rostamiani commented Jul 8, 2020

it works , but with a typescript error:

 Property 'options' does not exist on type 'Function'.

image

Update:
While serving, after recompiling this.$i18n is undefined error appears again.

@hayreenfly
Copy link

hayreenfly commented Mar 17, 2021

Had problems with this and i solved it with the following code:

const i18n = new VueI18n({
  locale: "en",
  messages: {
    en: {
      message: {
        hello: "hello world"
      }
    }
  }
});

Vue.customElement('my-widget', new App().$options, {
  beforeCreateVueInstance(RootComponentDefinition) {
    RootComponentDefinition.i18n = i18n
    return RootComponentDefinition
  }
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants