How to use i18n messages in a Nuxt3 pinia store
Asked Answered
K

2

6

When trying to use an i18n message in a Nuxt3 Pinia store I get the error message: "SyntaxError: Must be called at the top of a setup function"

When using a message in a template I can do:

<template>
 <p>{{ $t('welcome') }}</p>
</template>

But how to do it in the pinia store?

Here is what I tried in the store that led to the error:

 import { defineStore } from 'pinia';
const { t } = useI18n();

export const useAppStore = defineStore('appStore', {
  state: () => {
    return {
      label: $t('welcome')
    };
  },  
});

Koumis answered 3/12, 2023 at 15:12 Comment(1)
You better use t only in the template. Consider you have a label that has no translation like Car then the fallback will be Car, if there is an translation for the key Car it will use the translationAlicyclic
T
6

Most composables in Nuxt require application context to work with. Having code inside setup() option or using <script setup> tag in components ensures the context is known. It is also automatically available when the app is parsing templates - this is why it "just works" inside your template.

The problematic line is const { t } = useI18n(); because placed like this it is executed immideately upon loading the script and not after it is invoked from some component.

I find out following works for referencing I18n translation function from within scripts as $i18n object is being added into Nuxt application via the module and useNuxtApp() exposes the app instance to you:

export const useAppStore = defineStore('appStore', {
  state: () => {
    return {
      label: useNuxtApp().$i18n.t('welcome')
    };
  },  
});

Since I find the syntax a bit verbose, I usually create a composable wrapper around it to spare some characters:

export function useT (key: string): string {
  return useNuxtApp().$i18n.t(key)
}

Then I can only call useT('welcome')

Providing you invoke useAppStore() inside your component's setup, it should work fine, because the Pinia store shouldn't init prior to the first useAppStore() call.

Thing answered 3/12, 2023 at 16:16 Comment(4)
Thanks Ellrohir, accessing the message works fine with your solution. But when changing the language the message does not change. Im trying to access the message like this: <p>{{ appStore.label }}</p>Koumis
That would be because the outcome of t method is not reactive. I'd suggest to define an action on your store that will be triggered upon the language change and will translate all values again with the new locale.Thing
Strange that t is not reactive here. It makes the tasks more complicated than it could be. Lets hope it will get improved. Thanks for your help @Ellrohir.Koumis
I think it makes sense the return type is a "static" value. Because otherwise you would force all users to unwrap it when using t() in their scripts adding extra complexity to everyone. Maybe there are some extensions or plugins allowing reactivity. Something like "VueUse for I18n module". But I am not familiar with any such solution.Thing
S
2

Because of i18n requiring app context, I would recommend to use it in that app context. Technically - translation string does not denote state of application. I would rather return string identifier in label, and then would resolve it in component, that would display that text.

 import { defineStore } from 'pinia';
 const { t } = useI18n();

 export const useAppStore = defineStore('appStore', {
   state: () => {
     return {
       label: 'welcome'
     };
   },  
 });

Then, in component template it will be

<p>{{ $t(appStore.label) }}</p>

Sorry, it doesn't answer to question on how to use i18n in pinia store, but I think my solution will be sufficient for most use cases. If your specific case is not covered with it, I guess you will need to figure out reactivity as Ellrohir proposed in answer above

Sibel answered 4/12, 2023 at 9:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.