kazupon / vue-i18n

:globe_with_meridians: Internationalization plugin for Vue.js
https://kazupon.github.io/vue-i18n/
MIT License
7.28k stars 861 forks source link

Lazy loading a locale (setLocaleMessage) runs into "SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data" with InertiaJs #1186

Open lephro opened 3 years ago

lephro commented 3 years ago

Module versions (please complete the following information):

To Reproduce Steps to reproduce the behavior: ex:

  1. New Laravel Project with Inertia
  2. Use the Lazy-Loading code from the documentation
  3. Call "loadLanguageAsync" anywhere

Expected behavior The function should load the language and set the vue-i18n messages

Description After upgrading vue-i18n from 8.24.2 to 8.24.3 the loadLanguageAsync function / code from the documentation runs into an error: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data The stacktrace refers to the call of i18n.setLocaleMessage(lang, messages.default) inside the "loadLanguageAsync"-function and "crashes" (after some internal calls) at the render function off my main vue app. In this line: initialPage: JSON.parse(app.dataset.page), of the following code (initialization of vue app):

// Create Main Vue Instance
const el = document.getElementById("app");
new Vue({
  i18n,
  store,
  vuetify,
  render: (h) =>
    h(InertiaApp, {
      props: {
        initialPage: JSON.parse(app.dataset.page),
        resolveComponent: (name) =>
          import(`./Pages/${name}`).then(({ default: page }) => {
            page.layout = page.layout === undefined ? MainLayout : page.layout;
            return page;
          }),
      },
    }),
}).$mount(el);

Full stack trace

SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
    render app.js:71
    VueJS 9
        _render
        updateComponent
        get
        run
        flushSchedulerQueue
        nextTick
        flushCallbacks
        promise callback*timerFunc
        nextTick
    watchI18nData vue-i18n.esm.js:1354
    watchI18nData vue-i18n.esm.js:1353
    VueJS 10
        run
        flushSchedulerQueue
        nextTick
        flushCallbacks
        promise callback*timerFunc
        nextTick
        queueWatcher
        update
        notify
        set
    setLocaleMessage vue-i18n.esm.js:1882
    importLocale i18n.js:26
    promise callback*importLocale i18n.js:24
    loadLanguageAsync i18n.js:85
    selection LocaleSwitcher.vue:108
    VueJS 11
    set index.js:29
    updateSingle VItemGroup.js:200
    updateInternalValue VItemGroup.js:167
    onClick VItemGroup.js:103
    register VItemGroup.js:108
    VueJS 2
    toggle index.js:45
    click VListItem.js:98
    VueJS 2

This only happens with the latest release 8.24.3. Workes fine with 8.24.2.

Any ideas what could lead to this issue or how to solve it? I'm happy to provide any additional informations if need. The source of my project is unfortunatly closed, but its really just the lazyLoad code from the documentation in a new laravel project with jetstream (inertia) scaffolding.

package.json package.zip

kazupon commented 3 years ago

Thank you for your reporting!

That is very strange … 🤔 The only difference between v8.24.2 and v8.24.3 is performance improvement, as shown in the following link https://github.com/kazupon/vue-i18n/pull/1175/files

Also, the code for setLocaleMessage has not been modified recently. https://github.com/kazupon/vue-i18n/blame/v8.x/src/index.js#L757-L762

If I upgrade to the latest v8.24.4 version, will I still have issues?

lephro commented 3 years ago

I know its weird. I also looked in your changes and can't find a reason why this might happen.

With version v8.24.4 I get a slightly different error:

Stacktrace

ReferenceError: app is not defined
    render app.js:71
    VueJS 12
        _render
        updateComponent
        get
        run
        flushSchedulerQueue
        nextTick
        flushCallbacks
        promise callback*timerFunc
        nextTick
        queueWatcher
        update
        $forceUpdate
    watchI18nData vue-i18n.esm.js:1345
    VueJS 4
        nextTick
        flushCallbacks
        promise callback*timerFunc
        nextTick
    watchI18nData vue-i18n.esm.js:1344
    watchI18nData vue-i18n.esm.js:1343
    VueJS 10
        run
        flushSchedulerQueue
        nextTick
        flushCallbacks
        promise callback*timerFunc
        nextTick
        queueWatcher
        update
        notify
        set
    setLocaleMessage vue-i18n.esm.js:1872
    getStartingLocale i18n.js:37
    promise callback*getStartingLocale i18n.js:35
    <anonymous> i18n.js:14
    js app.js:839
    __webpack_require__ app.js:5995
    <anonymous> ErrorHandler.js:6
    js app.js:762
    __webpack_require__ app.js:5995
    <anonymous> API.js:5
    js app.js:641
    __webpack_require__ app.js:5995
    <anonymous> Config.js:5
    js app.js:652
    __webpack_require__ app.js:5995
    <anonymous> Config.js:5
    js app.js:883
    __webpack_require__ app.js:5995
    <anonymous> index.js:8
    js app.js:861
    __webpack_require__ app.js:5995
    <anonymous> app.js:4
    js app.js:806
    __webpack_require__ app.js:5995
    <anonymous> app.js:6308
    O app.js:6032
    <anonymous> app.js:6311
    <anonymous> app.js:6313

In app.js its the same line again

initialPage: JSON.parse(app.dataset.page),
const el = document.getElementById("app");
new Vue({
  i18n,
  store,
  vuetify,
  render: (h) =>
    h(InertiaApp, {
      props: {
        initialPage: JSON.parse(app.dataset.page),
        resolveComponent: (name) =>
          import(`./Pages/${name}`).then(({ default: page }) => {
            page.layout = page.layout === undefined ? MainLayout : page.layout;
            return page;
          }),
      },
    }),
}).$mount(el);

It runs through this vue-i18n part into e.$forceUpdate() ( watchI18nData vue-i18n.esm.js:1345 ), which is part of the changes of v8.24.3

VueI18n.prototype.watchI18nData = function watchI18nData () {
  var self = this;
  return this._vm.$watch('$data', function () {
    self._dataListeners.forEach(function (e) {
      Vue.nextTick(function () {
        e && e.$forceUpdate();
      });
    });
  }, { deep: true })
};

before 8.24.3 this function was

watchI18nData (): Function {
    const self = this
    return this._vm.$watch('$data', () => {
      let i = self._dataListeners.length
      while (i--) {
        Vue.nextTick(() => {
          self._dataListeners[i] && self._dataListeners[i].$forceUpdate()
        })
      }
    }, { deep: true })
  }

It must have to do with the forEach( (e) and $forceUpdate(), something must be different here.

Is this a vue-i18n problem? or do I have to do something different? or did we found some kind of edge-case error of vue? I have no idea :) You are the expert xD