[Nuxt, i18n, Vuex]: How to access this.$i18n.locales from store getter
Asked Answered
S

5

6

I'm building a web app using Nuxt v2.15 and @nuxtjs/i18n v7.2. I'm using Vuex for state management. In my global state, I want to create a getter that returns a value based on this.$i18n.locale.

What is the best way to access the current app context, which has $i18n attached to it, in my Vuex getter method?

nuxt.config.js

export default {
  modules: [
    "@nuxtjs/i18n", // https://i18n.nuxtjs.org/
  ],
  i18n: {
    locales: { /* configuration */ },
    defaultLocale: "en",
    detectBrowserLanguage: {
      fallbackLocale: "en",
      redirectOn: "root",
    },
    langDir: "~/locales/",
    vueI18n: {
      fallbackLocale: "en",
      messages: { /* translation data */ },
    },
  },
};

store/index.js

export const state = () => ({
  // Root module for Vuex store: https://nuxtjs.org/docs/directory-structure/store/
});

export const getters = {
  loginOpts() {
    const locale = this.$i18n.locale;
    const uiLocales = [locale];
    if (locale === "zh") {
      uiLocales.push("zh-CN");
    }
    return { params: { ui_locales: uiLocales } };
  },
};

components/login.vue

<template>
  <div>
    <v-btn v-if="$auth.loggedIn" @click="$auth.logout()">Sign Out</v-btn>
    <v-btn v-else @click="$auth.loginWith('auth0', $store.getters.loginOpts)">Sign In</v-btn>
  </div>
</template>

<script>
import { mapGetters } from "vuex";

export default {
  name: "Login",
  data() {
    return {};
  },
  computed: {
    ...mapGetters(["loginOpts"]),
  },
};
</script>

What I expect

I expect to be able to access this.$i18n from a Vuex getter, or to have some means of doing so.

What actually happens

In the getter method, this is undefined.

TypeError: Cannot read properties of undefined (reading '$i18n')

What I've tried

I see here that a getter is passed four arguments: state, getters, rootState, and rootGetters.

I've tried:

  • RTFM for Vuex State, Getters, and Nuxt i18n
  • accessing state.$i18n
  • adding $i18n: this.$i18n to the root state
Synesthesia answered 30/1, 2022 at 1:7 Comment(0)
C
3

I had a similar problem last week where I absolutely needed to use i18n inside a vuex getter.

I wouldn't recommend this as its probably not the most performant thing in the world but it worked for me and solved a huge problem while I was working on a government application.

components/login.vue

<template>
  <div>
    <v-btn v-if="$auth.loggedIn" @click="$auth.logout()">Sign Out</v-btn>
    <v-btn v-else @click="$auth.loginWith('auth0', theLoginOpts)">Sign In</v-btn>
  </div>
</template>

<script>
import { mapGetters } from "vuex";

export default {
  name: "Login",
  data() {
    return {
        theLoginOpts: $store.getters.loginOpts(this) // The secret sauce
    };
  },
  computed: {
    ...mapGetters(["loginOpts"]),
  },
};
</script>

store/index.js

export const state = () => ({
  // Root module for Vuex store: https://nuxtjs.org/docs/directory-structure/store/
});

export const getters = {
  loginOpts: (state) => (app) => { // App is the secret sauce from earlier
    const locale = app.$i18n.locale;
    const uiLocales = [locale];
    if (locale === "zh") {
      uiLocales.push("zh-CN");
    }
    return { params: { ui_locales: uiLocales } };
  },
};
Circumcise answered 30/1, 2022 at 14:27 Comment(2)
Thank you. That certainly is one way to do it, but I was hoping there was a more “built-in” approach. This is a workaround and gets the job done, but something about passing an argument to a getter doesn’t seem right to me…Synesthesia
Passing a param to a getter is something that can be done but is indeed a bit weird. Passing the actual context is even more hacky tbh.Mucor
S
1

After digging through docs, experimenting, and discussing with the folks who were kind enough to offer answers here, it is clear that i18n is not directly accessible in a Vuex getter method, despite the fact that @nuxt/i18n registers a Vuex module called i18n and everything I've read about Vuex modules suggests this should be possible.

I did however come across the docs for @nuxt/i18n callbacks, which led me to create a small plugin that sets the value of locale and uiLocales in the global state using a mutation.

The end result looks like this:

nuxt.config.js

export default {
  modules: [
    "@nuxtjs/i18n", // https://i18n.nuxtjs.org/
  ],
  plugins: [
    "~/plugins/i18n.js",
  ],
  i18n: {
    locales: { /* configuration */ },
    defaultLocale: "en",
    detectBrowserLanguage: {
      fallbackLocale: "en",
      redirectOn: "root",
    },
    langDir: "~/locales/",
    vueI18n: {
      fallbackLocale: "en",
      messages: { /* translation data */ },
    },
  },
};

plugins/i18n.js

export function findLocaleConfig (i18n, locale) {
  return (
    i18n.locales.find(({ iso, code }) => [iso, code].includes(locale)) || {}
  );
}

export default function ({ app }) {
  app.store.commit("localeChange", findLocaleConfig(app.i18n, app.i18n.locale));

  app.i18n.onLanguageSwitched = (oldLocale, newLocale) => {
    app.store.commit("localeChange", findLocaleConfig(app.i18n, newLocale));
  };
}

store/index.js

export const state = () => ({
  locale: null,
  uiLocales: [],
});

export const mutations = {
  localeChange(state, locale) {
    state.locale = locale.code;
    state.uiLocales = [locale.code, locale.iso];
  },
};

components/login.vue

<template>
  <div>
    <v-btn v-if="$auth.loggedIn" @click="$auth.logout()">Sign Out</v-btn>
    <v-btn v-else @click="$auth.loginWith('auth0', loginOpts)">Sign In</v-btn>
  </div>
</template>

<script>
export default {
  name: "Login",
  data() {
    return {};
  },
  computed: {
    loginOpts() {
      const uiLocales = this.$store.state.uiLocales;
      return { params: { ui_locales: uiLocales } };
    },
  },
};
</script>
Synesthesia answered 1/2, 2022 at 5:29 Comment(2)
Feel free to accept your own answer if it solved your issue! :)Mucor
It solved my issue, but I feel like it didn’t answer my question. I’d still like to know how to access the i18n Vuex module state from the root state, or understand why I cannot. Was holding out to see if someone knew.Synesthesia
M
0

You can access a the locale in an Vuex action with: this.app.i18n.locale.
This cannot be accessed in a state or getter (a getter is a not a place for it anyway IMO).

PS: The above means that means that you can access this value anywhere you have access to the Nuxt context.

Mucor answered 30/1, 2022 at 2:47 Comment(6)
Thank you. I’m curious why you think a getter is not the place for it. The user chooses a locale, which I would consider to be part of the application state. I’m trying to create a globally accessible computed value based on that state. How should this be implemented, in your opinion?Synesthesia
@ShaunScovil just access the vuex state. A getter is when you want to massage the state in some way, like filter an array based on something or alike. Here, you can conditionally (zh-CN) set your locale and access it afterwards, no need for a vuex getter. More info in the official docs: vuex.vuejs.org/guide/getters.html#gettersMucor
I see. I think the problem is that @nuxt/i18n is not using Vuex to store the locale state, and is instead storing it in app.$i18n. Maybe there is something I can do about that…Synesthesia
It can totally be stored in Vuex, check the various options. @ShaunScovilMucor
I’ve read the docs and there is an option for using Vuex that is enabled by default, but as far as I can tell it is inaccessible in the getter which prompted me to ask this question on SO. Docs: i18n.nuxtjs.org/options-reference/#vuexSynesthesia
My initial answer is still valid @ShaunScovilMucor
M
0

The simple way we did in our project is like below: In component computed prop

teamsForSelector() {
  return this.$store.getters['company/teamsForSelector'](this.$i18n);
}

and in state getters

teamsForSelector: (state) => ($i18n) => {
const teamsForSelector = state.teams.map((team) => {
  return {
    id: team.teamUid,
    label: team.teamName,
  };
});
teamsForSelector.unshift({
  id: 'all-teams',
  label: $i18n.t('key'),
});

return teamsForSelector; }

Multiplex answered 27/10, 2022 at 11:51 Comment(0)
R
0

In Nuxt 2 if your page renders on frontend you can try to access $i18n using window.$nuxt or simply $nuxt:

export const getters = {
  loginOpts() {
    const locale = window.$nuxt.$i18n.locale;
    ...
  },
};
Reduplication answered 6/9, 2023 at 11:49 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Recalcitrate

© 2022 - 2025 — McMap. All rights reserved.