vue-i18n icon indicating copy to clipboard operation
vue-i18n copied to clipboard

TypeError: _normalize is not a function

Open zkriszti opened this issue 2 years ago • 17 comments

Reporting a bug?

I'm migrating a Vue2 project from Webpack to Vite. We have been using vue-i18n extensively. We have a special folder structure, so after importing all translation files as modules with Vite's globEager, I am creating a messages object that should be used throughout the app (and it is the same structure that we have successfully used w/ Webpack). However, the translation strings in my object come back as a function, such as: title: (ctx) => {const { normalize: _normalize } = ctx;return _normalize(["My Translated Title"])} When I'm trying to use i18n in a component, I get the below message: TypeError: _normalize is not a function (and my component won't render).

Expected behavior

The above error message should not happen, component with vue-i18n based translation should render seamlessly.

Reproduction

Here's a link to a minimal reproducible example in codesandbox: https://codesandbox.io/s/mre-vue-i18n-normalize-is-not-a-function-duuxwt

i18n.js:

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '@intlify/vite-plugin-vue-i18n/messages'

//...

export default new VueI18n({
  locale: import.meta.VITE_I18N_LOCALE || locale,
  fallbackLocale: import.meta.VITE_I18N_FALLBACK_LOCALE || locale,
  messages: getLocaleMessages() // this returns custom messages object
})

vite.config.js

import { defineConfig } from 'vite'
import { createVuePlugin as vue } from "vite-plugin-vue2"
import vueI18n from '@intlify/vite-plugin-vue-i18n'
import path from "path";

export default defineConfig({
  define: {
    'process.env': {}
  },
  plugins: [
    vue(),
    vueI18n({
      compositionOnly: false, // set this to false to use Vue I18n Legacy API
      include: path.resolve(__dirname, './src/locales/**')
    })
  ],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src")
    }
  }
})

System Info

System:
    OS: Windows 10 10.0.19044
    CPU: (8) x64 Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz    
    Memory: 2.28 GB / 15.77 GB
  Binaries:
    Node: 12.14.1 - C:\Program Files\nodejs\node.EXE
    npm: 6.13.4 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 100.0.4896.127
    Edge: Spartan (44.19041.1266.0), Chromium (100.0.1185.44)
    Internet Explorer: 11.0.19041.1566

Screenshot

image

Additional context

No response

Validations

zkriszti avatar Apr 19 '22 20:04 zkriszti

EDIT: I added a link to a minimal reproducible example.

zkriszti avatar Apr 20 '22 09:04 zkriszti

Hi, I have the exact same issue, any news?

mmorainville avatar May 08 '22 17:05 mmorainville

You need to add vue-i18n-bridge package in your project. see the details: https://vue-i18n.intlify.dev/guide/migration/vue2.html#usage

If you do use it, note that there are some limitations.

vue-i18n-bridge has limitations: https://vue-i18n.intlify.dev/guide/migration/vue2.html#limitations

kazupon avatar May 25 '22 09:05 kazupon

I've been experiencing this exact issue today, and took the above sandbox and added vue-i18n-bridge following the instructions, and I still see the same error message...

Here's the updated sandbox:

https://codesandbox.io/s/mre-vue-i18n-normalize-is-not-a-function-forked-guqn1s

pedrolamas avatar Oct 16 '22 21:10 pedrolamas

I've been experiencing this exact issue today, and took the above sandbox and added vue-i18n-bridge following the instructions, and I still see the same error message...

Here's the updated sandbox:

https://codesandbox.io/s/mre-vue-i18n-normalize-is-not-a-function-forked-guqn1s

Exactly, I've also tried using vue-i18n-bridge as per Kazupon's suggestion, but it unfortunately didn't solve the issue in my case either.

zkriszti avatar Oct 17 '22 08:10 zkriszti

Ok, I managed to bypass this completely... my issue was that the our translations are on yaml files, so once I used a yaml transformer (even removed @intlify/vite-plugin-vue-i18n completely!), all issues were gone and everything started to work as expected!

pedrolamas avatar Oct 17 '22 17:10 pedrolamas

Really important to say that adding that configuration of vue-i18n to vite.config.js doesn't solves this problem.

NathanAP avatar Nov 24 '22 13:11 NathanAP

I'm also migrating a vue 2.7 app from webpack to vite.

I Have been stuck on this error for hours 😕. Any update? I followed the migration guide without success.

I'm still getting TypeError: _normalize is not a function

Adnan-kreiker avatar Nov 25 '22 16:11 Adnan-kreiker

After we run npm run build, this is very problematic.

I'm not sure if I'm not importing my messages correctly, but why are all of them functions? If I have this structure:

{ 
   "en-EN": { 
      "test": "this is a test"
   }
}

the return of importedTest['en-EN'].test is not this is a test. It is actually e=>{const{normalize:n}=e;return n(["this is a test])}. Why this occurs? Am I doing something wrong?

Edit: here is an image of one of my imported JSON:

image

NathanAP avatar Dec 12 '22 19:12 NathanAP

Nuxt3 with some trick nice work...

<!-- <i18n src="./menu.json"></i18n> -->

<i18n lang="json">
  {
    "id": {
      "name": "Pertama",
      "sub": [
        {
          "one": "Satu"
        }
      ]
    },
    "en": {
      "name": "2nd",
      "sub": [
        {
          "one": "One"
        }
      ]
    }
  }
</i18n>

<script setup lang="ts">
const { locale, t, getLocaleMessage } = useI18n()
const menu = getLocaleMessage(unref(locale))

function normalize([v]: string[]) {
  return v
}
</script>

<template>
  <div>
    <div>
      {{ t('name') }}
    </div>
    <div v-for="(item, i) in menu.sub" :key="i">
      Sub: {{ item.one({ normalize }) }}
    </div>
  </div>
</template>

kenhyuwa avatar Jan 18 '23 04:01 kenhyuwa

Nuxt3 with some trick nice work...

<!-- <i18n src="./menu.json"></i18n> -->

<i18n lang="json">
  {
    "id": {
      "name": "Pertama",
      "sub": [
        {
          "one": "Satu"
        }
      ]
    },
    "en": {
      "name": "2nd",
      "sub": [
        {
          "one": "One"
        }
      ]
    }
  }
</i18n>

<script setup lang="ts">
const { locale, t, getLocaleMessage } = useI18n()
const menu = getLocaleMessage(unref(locale))

function normalize([v]: string[]) {
  return v
}
</script>

<template>
  <div>
    <div>
      {{ t('name') }}
    </div>
    <div v-for="(item, i) in menu.sub" :key="i">
      Sub: {{ item.one({ normalize }) }}
    </div>
  </div>
</template>

Use .js file instead of json, then getLocaleMessage should works fine

Stein-Hakase avatar Apr 04 '23 10:04 Stein-Hakase

Any update on this? Is there a solution to the problem?

I'm facing the same error (vue 2.7 migrating to vite)

gazben avatar May 16 '23 14:05 gazben

None of the solutions here really solve my problem. Is this still being worked on?

spectrachrome avatar Jun 16 '23 10:06 spectrachrome

@gazben @spectrachrome Please check this issue

I found a temporary hack to get around it, hoping for it to get solved one day

Adnan-kreiker avatar Jun 16 '23 20:06 Adnan-kreiker

Facing same issue while migrating vite with Vue 2.7. :(

I found a hack to workaround this problem in vue 2.7. I'm using import.meta.glob() to import my .js localization files. They export default an object containing all my translation messages. So they look like this:

export default {
  ok: 'Ok',
....
};

This is how I import my localization .js files:

async function loadLocale(locale) {
  let modules;
  let resp = {};
  if (locale === 'de') {
    modules = import.meta.glob('../locales/de.js');
  } else {
    modules = import.meta.glob('../locales/en.js');
  }
  await modules[Object.keys(modules)[0]]().then((mod) => {
    // console.log(Object.keys(modules)[0], mod.default);
    resp = mod.default;
  });
  applySourcesHack(resp);
  return resp;
}

Before applying the locale(s) to i18n, I have to apply the hack. The problem is, that some keys contain functions instead of strings. This is the hack:

function applySourcesHack(obj) {
  const getSource = (func) => {
    if (typeof func === 'function') {
      return '' + func.source;
    }
    return 'not a function';
  };

  if (typeof obj === 'object') {
    Object.keys(obj).forEach((key) => {
      if (typeof obj[key] === 'object') {
        applySourcesHack(obj[key]);
      } else {
        obj[key] = getSource(obj[key]);
      }
    });
  }
}

I also had to use this bundler for the vite bridge:

vite.config.js

...
resolve: {
    alias: {
      'vue-i18n-bridge': 'vue-i18n-bridge/dist/vue-i18n-bridge.esm-bundler.js',
    },
  },

Hope this will help someone :)

Note: I'm only using vue 2.7 as an intermediate step to upgrade to vue 3.x so I'm fine with using hacks like this.

Instinctlol avatar Sep 14 '23 07:09 Instinctlol

Ran into this today. We were trying to integrate a Vue3 web component (our component, running Vue3 with i18n) into a Magento store (running Vue Storefront with i18n). It seems that something in the Magento/Vue codebase is messing with the message compilation of our component.

A work around that we have now is to enable the JIT compilation, which forces a different message compiler and works. You can do this by setting the global __INTLIFY_JIT_COMPILATION__ flag to true before loading your code.

dgoemans avatar Jan 02 '24 12:01 dgoemans