i18next / i18next-vue

Internationalization for Vue 2 & 3 using the i18next ecosystem
https://i18next.github.io/i18next-vue/
MIT License
74 stars 8 forks source link

The `i18next` object returned from the `useTranslation()` is not reactive. #6

Closed YehorPytomets closed 2 years ago

YehorPytomets commented 2 years ago

🐛 Bug Report

Hello!

When using i18next object returned from the useTranslation() in computed properties to get the current language, for example in a language switcher component, we expect it to be reactive as docs in the index.ts specify. But it it isn't.

To Reproduce

For example, in the following component, neither of the computed properties will be updated after the language change:

<template>
    <p>firstLanguage: {{firstLanguage}}</p>
    <p>resolvedLanguage: {{resolvedLanguage}}</p>
    <p>language: {{language}}</p>
    <button @click="translateToChinese">Zh</button>
    <button @click="translateToEnglish">En</button>
</template>
<script setup>
    import {computed} from 'vue';
    import {useTranslation} from 'i18next-vue';

    const {i18next} = useTranslation();
    const firstLanguage = computed(() => i18next.languages[0]);
    const resolvedLanguage = computed(() => i18next.resolvedLanguage);
    const language = computed(() => i18next.language);

    function translateToChinese() {
        i18next.changeLanguage('zh');
    }

    function translateToEnglish() {
        i18next.changeLanguage('en');
    }
</script>

But the t() function updates as expected. To temporary work around this issue we've created our local composable that makes the i18next reactive by subscribing to common i18next events:

import {readonly, shallowRef, triggerRef} from "vue";
import {useTranslation} from "i18next-vue";

/**
 * Composable that makes the `i18next` object reactive, allowing to trigger language updates.
 *
 * <p>Exposes the `t()` function and `i18next` object from the `useTranslation()` composable,
 * but with reactive `i18next`.
 */
export default function useI18next() {

    const {i18next: i18nextOriginal, t} = useTranslation();
    const i18next = shallowRef(i18nextOriginal);

    // watch for common i18next events: https://www.i18next.com/overview/api#events
    i18nextOriginal.on("languageChanged", update);
    i18nextOriginal.on("loaded", update);
    i18nextOriginal.store.on("added", update);
    i18nextOriginal.store.on("removed", update);

    function update() {
        triggerRef(i18next); // trigger update
    }

    return {
        i18next: readonly(i18next), // prohibits changing `i18next` value in components
        t,
    }
}

Could you please fix that and make the i18next returned from useTranslation() reactive, or clarify why it is not reactive if it is so by design and describe this in the documentation explicitly. Because the composable name makes us believe that the returned values are reactive, if not stated the opposite. Hope our solution helps in some way.

I would be grateful to get any feedback on that.

Expected behavior

It is expected that the i18next is reactive and can be used in computed properties to get the current language.

Your Environment

kkuegler commented 2 years ago

Thanks for the bug report!

This was indeed missing in the 2.0.0-beta.0 release. The code - as you saw - was already there on main. I just cut a 2.0.0-beta.1 release to include the i18next object's reactivity.

All computed properties from you example should now update when the language is changed.

Happy to hear back if this solves the problem for you.

YehorPytomets commented 2 years ago

@kkuegler, thank you for your response!

Indeed, the new version worked for us and the i18next object is now reactive. Thank you for your solution.

P.S. I suppose the issue can now be closed.

kkuegler commented 2 years ago

Glad to hear, it works for you :)