nuxt-modules / i18n

I18n module for Nuxt
https://i18n.nuxtjs.org
MIT License
1.72k stars 477 forks source link

RangeError: Maximum call stack size exceeded on any version after 6.17.0 #1075

Closed nelsonlarocca closed 3 years ago

nelsonlarocca commented 3 years ago

Version

nuxt-i18n: any version after 6.17.0 nuxt: 2.14.2 and 2.15.2

Nuxt-i18n configuration

inside a component we have:

methods: { async switLang(lang) { await this.$i18n.setLocale(lang) } }

I'm just using this.$i18n.setLocale(language); and it works 100% fine only IF "nuxt-i18n": "^6.17.0", any other version after that drops the error. Maybe not the first time but randomly after repeating some times the lang switching

image

I use no_prefix strategy any help? Thanks

rchl commented 3 years ago

Can you show the full source code of that component?

nelsonlarocca commented 3 years ago

sure, here you have thanks

<template>
  <div>
    <div>
      <v-select
        v-model="xxx"
        color="primary"
        solo
        :items="availableLocales"
        item-value="code"
        item-text="name"
        @change="changedValue"
      />
    </div>
  </div>
</template>

<script>
export default {
  props: {
    pLang: {
      type: String,
      default: '',
    },
  },

  data() {
    return {
      xLang: 'xxx',
    }
  },

  computed: {
    xxx: {
      get() {
        return this.$i18n.locale
      },
      set(v) {},
    },

    availableLocales() {
      return this.$i18n.locales // .filter((i) => i.code !== this.$i18n.locale)
    },
  },

  mounted() {
    this.xLang = this.pLang || this.$i18n.locale
  },

  methods: {
    async changedValue(lang) {
      if (lang !== this.$i18n.locale) {
        // asking to avoid looping if other selector on same page
        await this.$i18n.setLocale(lang)
        this.$bus.$emit('langSwitched', { location: 'FOOTER', lang })
      }
    },
  },
}
</script>
rchl commented 3 years ago

Sorry but need more information.

klona is only used to clone the i18n.locales and i18n.vueI18n objects so there could be something in those objects that causes that.

If you could log the i18n.locales value before this error then it might reveal some issue with the circular property.

Ideally, you would create a repo that reproduces as that would save a lot of time in trying to figure this one out.

PolarWooolf commented 3 years ago

I have the same problem. It appears when observing i18n.locales object изображение Klona just starting infinitly cloning recursive object

nelsonlarocca commented 3 years ago

@PolarWooolf, when is the error thrown? I used to have a v-select to switch the current language, but I couldn't find the problem itself after debugging everything. BUT if using i18n version 6.17.0, no issues appeared.

I changed the Vuetify v-select to a radio version, and everything is fine now.

PolarWooolf commented 3 years ago

Stack trace: изображение I'm not sure what's really creating this observer in my app, but before it dies it's trying to clone x.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.__ob__.value.... object, that initial klona's call here: изображение

rchl commented 3 years ago

Thanks for that information. Should be very helpful in reproducing and fixing.

It looks like you are also using i18n block in that component because that's when extend is called.

rchl commented 3 years ago

Created a fix in https://github.com/nuxt-community/i18n-module/pull/1090

So it's enough to make the i18n.locales object reactive to trigger the issue. I had one place where I didn't use klona so fixed that now.

Tofandel commented 2 years ago

This issue is still happening in v8.27.2

If you run twice

const msg = {'test': 'test'};
i18n.setLocaleMessage('en', msg);
i18n.setLocaleMessage('en', msg);

Then msg gets reactive after the first call, and the second call results in an error

rchl commented 2 years ago

This is an API provided by https://github.com/kazupon/vue-i18n so you could report it there. I suppose ideally it would copy the provided messages object instead of unintentionally mutating it.

But I do think that it should be possible to avoid doing that on your side as a workaround. Is there any good reason for assigning the same messages object twice?

FYI @kazupon

Tofandel commented 2 years ago

Indeed, this is the wrong repo, sorry, I just came here from googling this issue and didn't check the url

It's weird because I've been trying to get it to reproduce on codesandbox but I just can't, it's happening on a large codebase and I'm thinking it's only happening with a weird combo of things

I was originally just switching locales multiple times and the loader wasn't checking if the locale had already been loaded (it's lazy loading the locale from a json file)

import axios from 'rewart-frontend-library/src/plugins/axios';
import Vue from 'vue';
import VueI18n from 'vue-i18n';

import vuetify from '@/plugins/vuetify';

Vue.use(VueI18n);

function loadLocaleMessages(locale) {
  return Promise.all([
    import(
      /* webpackChunkName: "app-locale-[request]" */
      /* webpackInclude: /(en|hu).json$/ */
      /* webpackMode: "lazy" */
      /* webpackPreload: true */
      `@/locales/${locale}.json`),
    import(
      /* webpackChunkName: "vuetify-locale-[request]" */
      /* webpackInclude: /(en|hu).js$/ */
      /* webpackMode: "lazy" */
      /* webpackPreload: true */
      `vuetify/lib/locale/${locale}.js`).then((v) => v.default),
  ]).then(([msg, vuetify]) => i18n.setLocaleMessage(locale, {...msg, $vuetify: vuetify})); // Error happening here
}

let fallbackLoaded = false;
let fallbackLoading = false;

const i18n = new VueI18n({
  locale: null,
  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: {},
  silentFallbackWarn: true,
  missing(locale) {
    if (!fallbackLoaded && !fallbackLoading && locale !== i18n.fallbackLocale) {
      fallbackLoading = true;
      loadLocaleMessages(i18n.fallbackLocale).then(() => fallbackLoaded = true);
    }
  },
});

export const switchLocale = (locale = process.env.VUE_APP_I18N_LOCALE || 'en') => {
  const lang = locale.slice(0, 2);

  // If the same language
  if (i18n.locale === lang) {
    return Promise.resolve();
  }
  return loadLocaleMessages(lang).then(() => {
    axios.defaults.headers.common['Accept-Language'] = lang;
    document.querySelector('html').setAttribute('lang', lang);
    i18n.locale = lang;
    vuetify.framework.lang.current = locale;
  });
};

export default i18n;

And so once the same locale gets loaded again this error happens (maybe it has to do with webpack modules)

As a workaround I indeed just keep a variable of the loaded locales to avoid loading the same one multiple times