How to use i18n messages in a Nuxt3 pinia store

535 views Asked by At

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')
    };
  },  
});

2

There are 2 answers

4
Ellrohir On BEST ANSWER

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.

0
HappiSmyle On

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