intlify / vue-i18n

Vue I18n for Vue 3
https://vue-i18n.intlify.dev/
MIT License
2.1k stars 323 forks source link

`i18n.global.locale` type definiton wrong #1003

Open WangHansen opened 2 years ago

WangHansen commented 2 years ago

Reporting a bug?

When I implemented a function to globally switch locale as follows:

截屏2022-05-07 下午11 31 39

The type definition for i18n.global.locale is wrong:

截屏2022-05-07 下午11 32 31

By printing out the console, I was able to see that i18n.global.locale should be a computed value

Expected behavior

TypeScript not throw error

Reproduction

Type definition problem

System Info

System:
  OS: macOS 12.3.1
  CPU: (8) x64 Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz
  Memory: 171.65 MB / 16.00 GB
  Shell: 5.8 - /bin/zsh
Binaries:
  Node: 16.13.2 - ~/.nvm/versions/node/v16.13.2/bin/node
  Yarn: 1.22.10 - /usr/local/bin/yarn
  npm: 8.6.0 - ~/.nvm/versions/node/v16.13.2/bin/npm
  Watchman: 2021.10.18.00 - /usr/local/bin/watchman
Browsers:
  Brave Browser: 91.1.26.77
  Chrome: 101.0.4951.54
  Safari: 15.4

"vue": "^3.2.25",
"vue-i18n": "^9.1.10",
"@intlify/vite-plugin-vue-i18n": "^3.4.0",
"@vitejs/plugin-vue": "^2.3.1",

Screenshot

No response

Additional context

No response

Validations

kazupon commented 2 years ago

Thank you for your reporting!

I seem that the i18n instance is created with createI18n with legacy: true option.

The global property can refer to Composer or VueI18n, as described in the API docs. https://vue-i18n.intlify.dev/api/general.html#global

You can check the mode of an i18n instance with mode. https://vue-i18n.intlify.dev/api/general.html#mode

WangHansen commented 2 years ago

Thank you for your reporting!

I seem that the i18n instance is created with createI18n with legacy: true option.

The global property can refer to Composer or VueI18n, as described in the API docs. https://vue-i18n.intlify.dev/api/general.html#global

You can check the mode of an i18n instance with mode. https://vue-i18n.intlify.dev/api/general.html#mode

Sorry, should specify, I actually created with legacy: false:

const i18n = createI18n<I18nOptions, [MessageSchema], 'zh-CN' | 'en-CA'>({
  legacy: false,
  locale: getLang(),
  messages: {
    'zh-CN': zhCN,
    'en-CA': enCA,
  },
  // something vue-i18n options here ...
});
markbrockhoff commented 2 years ago

Hi, I was facing the same problem on vue-i18n 9.2.0-beta.31.

I had a look at the type of i18n.global.locale and it's of type string | WritableComputedRef. So you can't access .value unless you tell Typescript that i18n.global.locale is not a string but a ref. Therefore I just added a typeguard using the isRef function from vue:

export const changeLocale = (i18n: I18n, locale: string) => {
  if (i18n.mode === 'legacy') {
    i18n.global.locale = locale;
  } else if (isRef(i18n.global.locale)) {
    i18n.global.locale.value = locale;
  }
};

That way the type changes to Ref inside the else block. But this behaviour should be documented as the example in the Documentation does currently not work for Typescript.

leo-martin commented 2 years ago

Hi I am facing the same issue.

I'm using i18n 9.1.10 (fixed version)

Here is my i18n setup

const i18n = createI18n({
  locale: 'fr',
  legacy: false,
  fallbackLocale: 'en',
  globalInjection: true,
  messages: messages as LocaleMessages<VueMessageType>,
})

In the same file, I try to access and modify the locale using i18n.global.locale. When I check in my code, the type for i18n.global.locale is a ref but if I try i18n.global.locale.value = 'fr' then I get a console error saying i18n.global.locale is a String and if I log i18n.global.locale.value I get undefined

I've checked and I'm using the composition mode (i18n.mode is composition) . I've tried checking with isRef, and it always returns false even though mode is composition.

Any help is appreciated

porkus1990 commented 2 years ago

Have the same problem with next-version. With legacy: false i have to set it with i18n.global.locale.value = locale;

valgeirb commented 2 years ago

I'm facing the same problem I think, getting an unknown type from locale which is causing errors with v-model:

Screenshot 2022-08-05 at 10 21 41 Screenshot 2022-08-05 at 10 23 01

A workaround for me is using the implicit way:

<RadioGroup v-model="$i18n.locale">
timothymctim commented 2 years ago

To me it’s clear that this bug only occurs when useScope: 'global' in useI18n is specified. That is, with const { locale } = useI18n() I get const locale: WritableComputedRef<string>, while with const { locale } = useI18n({ useScope: 'global' }); I get const locale: WritableComputedRef<unknown>.

it-xtech-dev commented 2 years ago

I'm using vue-i18n@9.2.2 and having issues with global.locale type. Type recognition says its a string while with legacy: false should be ref<string>: Zrzut ekranu 2022-08-30 o 19 26 23

rfox12 commented 2 years ago

@kazupon I believe @timothymctim has laid out the scenario quite well. Non-legacy users are having trouble getting Locale to type correctly to WritableComputedRef<Locale>

kazupon commented 2 years ago

sorry, I'm focusing on nuxtjs/i18n for v8 alpha release. please

Type definitions in vue-i18n are really complex. I’m not a full-time OSS yet, so it may take some time until I'm starting on this issue.

mberrg commented 1 year ago

I'm experiencing the same problem in 9.2.2

Any update on this?

timothymctim commented 1 year ago

I’m currently working around this issue by exporting the result of createI18n in a separate file and importing this in main.ts (where you create the app and call app.use(i18n);) and wherever I need access to the global i18n object. For example,

import i18n from 'path/to/file/calling/createI18n.ts';

function myFunctionNeedingTheGlobalI18nObject() {
  const { locale } = i18n.global;  // or anything else you need
  // rest of code
}

Here, locale now types as const locale: WritableComputedRef<string>.

benlind commented 1 year ago

I was also encountering this issue:

Argument of type 'string' is not assignable to parameter of type 'Ref'.

Passing legacy: false to createI18n fixed it for me.

Narkoleptika commented 8 months ago

I also ran into this issue while trying to define types using createI18n<>. I was already passing legacy: false and that was working ok, but as soon as I added the type definition in the brackets <>, it started throwing type errors. Turns out you also need to pass the legacy option as part of the type definition.

Using the example from the docs

const i18n = createI18n<[MessageSchema], 'en-US' | 'ja-JP', false>({
  legacy: false,
  locale: 'en-US',
  messages: {
    'en-US': enUS
  }
})

Notice the false after 'en-US' | 'ja-JP'

Adding that fixed the issue for me.

ibrahimBeladi commented 3 months ago

Even with passing that legacy is false in the types parameters/arguments, the Locale is still string in the template I am on v9.13.1

image image