{{ $t("insurance") }}
74 | 75 | ``` 76 | 77 | `$t()` works both in Options API and Composition API components. 78 | 79 | Using the [`useTranslation()` composition function](https://i18next.github.io/i18next-vue/guide/composition-api.html) you can access the i18next instance and `t()` in the `setup` part, and e.g. get a `t()` functions for a specific namespace. 80 | 81 | ```vue 82 | 88 | 89 | 90 |inline: {{ t("insurance") }}
92 |inline with $t: {{ $t("insurance") }}
93 |computed: {{ term }}
94 | 95 | ``` 96 | 97 | ### Translation component 98 | 99 | i18next-vue provides a [`{{ t(k) }}
", 10 | props: ["k"], 11 | setup() { 12 | return useTranslation(); 13 | }, 14 | }; 15 | 16 | test("Simple translate", async () => { 17 | const i18next = globalI18next.createInstance(); 18 | await i18next.init({ 19 | lng: "en", 20 | resources: { 21 | en: { translation: { hello: "Hello world" } }, 22 | }, 23 | }); 24 | 25 | const wrapper = mount(CompositionWithKey, { 26 | props: { 27 | k: "hello", 28 | }, 29 | global: { 30 | plugins: [[i18nextvue, { i18next }]], 31 | }, 32 | }); 33 | 34 | expect(wrapper.text()).toBe("Hello world"); 35 | }); 36 | 37 | test("Language change", async () => { 38 | const i18next = globalI18next.createInstance(); 39 | await i18next.init({ 40 | lng: "en", 41 | resources: { 42 | en: { translation: { hello: "Hello world" } }, 43 | de: { translation: { hello: "Hallo Welt" } }, 44 | }, 45 | }); 46 | 47 | const wrapper = mount(CompositionWithKey, { 48 | props: { 49 | k: "hello", 50 | }, 51 | global: { 52 | plugins: [[i18nextvue, { i18next }]], 53 | }, 54 | }); 55 | 56 | expect(wrapper.text()).toBe("Hello world"); 57 | 58 | await i18next.changeLanguage("de"); 59 | await expectText(wrapper, "Hallo Welt"); 60 | 61 | await i18next.changeLanguage("en"); 62 | await expectText(wrapper, "Hello world"); 63 | }); 64 | 65 | test("$i18next reactivity", async () => { 66 | const LangComponent = { 67 | template: "{{ i18next.language }}
", 68 | setup() { 69 | return useTranslation(); 70 | }, 71 | }; 72 | const i18next = globalI18next.createInstance(); 73 | await i18next.init({ 74 | lng: "en", 75 | resources: {}, 76 | }); 77 | 78 | const wrapper = mount(LangComponent, { 79 | global: { 80 | plugins: [[i18nextvue, { i18next }]], 81 | }, 82 | }); 83 | 84 | expect(wrapper.text()).toBe("en"); 85 | 86 | await i18next.changeLanguage("de"); 87 | await expectText(wrapper, "de"); 88 | 89 | await i18next.changeLanguage("en"); 90 | await expectText(wrapper, "en"); 91 | }); 92 | 93 | test("Composition-t with set namespace", async () => { 94 | const CompositionWithNsp = { 95 | template: "local: {{ t(k) }} - global: {{ $t(k) }}
", 96 | props: ["k"], 97 | setup() { 98 | return useTranslation("nsp2"); 99 | }, 100 | }; 101 | 102 | const i18next = globalI18next.createInstance(); 103 | await i18next.init({ 104 | lng: "en", 105 | resources: { 106 | en: { 107 | translation: { hello: "Hello world" }, 108 | nsp2: { hello: "Greetings globe!" }, 109 | }, 110 | }, 111 | }); 112 | 113 | const wrapper = mount(CompositionWithNsp, { 114 | props: { 115 | k: "hello", 116 | }, 117 | global: { 118 | plugins: [[i18nextvue, { i18next }]], 119 | }, 120 | }); 121 | 122 | expect(wrapper.text()).toBe("local: Greetings globe! - global: Hello world"); 123 | }); 124 | 125 | test("Composition-t with set language", async () => { 126 | const Component = { 127 | template: "local: {{ t('hello') }} - global: {{ $t('hello') }}
", 128 | setup() { 129 | return useTranslation(undefined, { lng: "de" }); 130 | }, 131 | }; 132 | 133 | const i18next = globalI18next.createInstance(); 134 | await i18next.init({ 135 | lng: "en", 136 | resources: { 137 | en: { translation: { hello: "Hello world" } }, 138 | de: { translation: { hello: "Hallo Welt" } }, 139 | }, 140 | }); 141 | 142 | const wrapper = mount(Component, { 143 | global: { 144 | plugins: [[i18nextvue, { i18next }]], 145 | }, 146 | }); 147 | 148 | expect(wrapper.text()).toBe("local: Hallo Welt - global: Hello world"); 149 | }); 150 | 151 | test("Translation with only a provide context and no component", async () => { 152 | const i18next = globalI18next.createInstance(); 153 | await i18next.init({ 154 | lng: "en", 155 | resources: { 156 | en: { translation: { hello: "Hello world" } }, 157 | }, 158 | }); 159 | 160 | const result = ref(""); 161 | mount(CompositionWithKey, { 162 | props: { 163 | k: "hello", 164 | }, 165 | global: { 166 | plugins: [ 167 | [i18nextvue, { i18next }], 168 | // fake plugin that uses the translation outside of a component/setup context 169 | { 170 | install(app) { 171 | app.runWithContext(() => { 172 | const { t } = useTranslation(); 173 | result.value = t("hello"); 174 | }); 175 | }, 176 | }, 177 | ], 178 | }, 179 | }); 180 | 181 | expect(result.value).toBe("Hello world"); 182 | }); 183 | -------------------------------------------------------------------------------- /tests/escaping.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { expect, test } from "vitest"; 3 | import globalI18next from "i18next"; 4 | import i18nextvue from "../index"; 5 | 6 | export const TranslateWithKeyAndValue = { 7 | template: "{{ $t(k, {val: v}) }}
", 8 | props: ["k", "v"], 9 | }; 10 | 11 | test("Vue text escaping without value", async () => { 12 | const i18next = globalI18next.createInstance(); 13 | await i18next.init({ 14 | lng: "en", 15 | interpolation: { 16 | escapeValue: false, 17 | }, 18 | resources: { 19 | en: { translation: { hello: "HelloHello <world>
"); 33 | }); 34 | 35 | test("Default config will yield double value escaping", async () => { 36 | const i18next = globalI18next.createInstance(); 37 | await i18next.init({ 38 | lng: "en", 39 | resources: { 40 | en: { translation: { hello: "Hello {{val}}" } }, 41 | }, 42 | }); 43 | 44 | const wrapper = mount(TranslateWithKeyAndValue, { 45 | props: { 46 | k: "hello", 47 | v: "Hello <world>
"); 55 | }); 56 | 57 | test("Recommended config with proper escaping", async () => { 58 | const i18next = globalI18next.createInstance(); 59 | await i18next.init({ 60 | lng: "en", 61 | interpolation: { 62 | escapeValue: false, 63 | }, 64 | resources: { 65 | en: { translation: { hello: "Hello {{val}}" } }, 66 | }, 67 | }); 68 | 69 | const wrapper = mount(TranslateWithKeyAndValue, { 70 | props: { 71 | k: "hello", 72 | v: "Hello <world>
"); 80 | }); 81 | -------------------------------------------------------------------------------- /tests/helpers.ts: -------------------------------------------------------------------------------- 1 | import { flushPromises, VueWrapper } from "@vue/test-utils"; 2 | import { expect } from "vitest"; 3 | import type { BackendModule, ReadCallback, ResourceKey } from "i18next"; 4 | 5 | export const TranslateWithKey = { 6 | template: "{{ $t(k) }}
", 7 | props: ["k"], 8 | }; 9 | 10 | export async function expectText(wrapper: VueWrapper, expected: string) { 11 | await flushPromises(); // await potential Vue re-rendering 12 | expect(wrapper.text()).toBe(expected); 13 | } 14 | 15 | export async function expectHTML(wrapper: VueWrapper, expected: string) { 16 | await flushPromises(); // await potential Vue re-rendering 17 | expect(wrapper.html()).toBe(expected); 18 | } 19 | 20 | export function withTimeout(code: () => void) { 21 | return new PromiseInitialized
Loading
', 10 | }; 11 | 12 | const i18next = globalI18next.createInstance(); 13 | const backend = new LoadingModule(); 14 | i18next.use(backend).init({ 15 | lng: "en", 16 | fallbackLng: false, 17 | }); 18 | 19 | const wrapper = mount(Component, { 20 | global: { 21 | plugins: [[i18nextvue, { i18next }]], 22 | }, 23 | }); 24 | expect(!!i18next.isInitialized).toBe(false); 25 | await expectText(wrapper, "Loading"); 26 | 27 | await withTimeout(() => { 28 | backend.fakeLoaded("en", "translation", {}); 29 | }); 30 | 31 | expect(i18next.isInitialized).toBe(true); 32 | await expectText(wrapper, "Initialized"); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/loading.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { expect, test } from "vitest"; 3 | import globalI18next from "i18next"; 4 | import i18nextvue, { useTranslation } from "../index"; 5 | import { LoadingModule, TranslateWithKey, withTimeout, expectText } from "./helpers"; 6 | 7 | test("Simple translate", async () => { 8 | const i18next = globalI18next.createInstance(); 9 | const backend = new LoadingModule(); 10 | i18next.use(backend).init({ 11 | lng: "en", 12 | fallbackLng: false, 13 | }); 14 | 15 | const wrapper = mount(TranslateWithKey, { 16 | props: { 17 | k: "hello", 18 | }, 19 | global: { 20 | plugins: [[i18nextvue, { i18next }]], 21 | }, 22 | }); 23 | expect(wrapper.text()).toBe(""); 24 | 25 | await withTimeout(() => { 26 | backend.fakeLoaded("en", "translation", { hello: "Hello world" }); 27 | }); 28 | 29 | await expectText(wrapper, "Hello world"); 30 | }); 31 | 32 | test("Language change", async () => { 33 | const i18next = globalI18next.createInstance(); 34 | const backend = new LoadingModule(); 35 | const initPromise = i18next.use(backend).init({ 36 | lng: "en", 37 | fallbackLng: false, 38 | }); 39 | await withTimeout(() => { 40 | backend.fakeLoaded("en", "translation", { hello: "Hello world" }); 41 | }); 42 | await initPromise; 43 | 44 | const wrapper = mount(TranslateWithKey, { 45 | props: { 46 | k: "hello", 47 | }, 48 | global: { 49 | plugins: [[i18nextvue, { i18next }]], 50 | }, 51 | }); 52 | 53 | expect(wrapper.text()).toBe("Hello world"); 54 | 55 | i18next.changeLanguage("de"); 56 | 57 | // remains en, as de is not yet loaded 58 | await expectText(wrapper, "Hello world"); 59 | 60 | await withTimeout(() => { 61 | backend.fakeLoaded("de", "translation", { hello: "Hallo Welt" }); 62 | }); 63 | 64 | // now it actually is de 65 | await expectText(wrapper, "Hallo Welt"); 66 | 67 | await i18next.changeLanguage("en"); 68 | await expectText(wrapper, "Hello world"); 69 | }); 70 | 71 | test("Composition-t, loading namespace", async () => { 72 | const CompositionWithNsp = { 73 | template: "t: {{ t(k) }} - $t: {{ $t(k) }}
", 74 | props: ["k"], 75 | setup() { 76 | return useTranslation("nsp2"); 77 | }, 78 | }; 79 | 80 | const i18next = globalI18next.createInstance(); 81 | const backend = new LoadingModule(); 82 | const initPromise = i18next.use(backend).init({ 83 | lng: "en", 84 | fallbackLng: false, 85 | }); 86 | 87 | const wrapper = mount(CompositionWithNsp, { 88 | props: { 89 | k: "hello", 90 | }, 91 | global: { 92 | plugins: [[i18nextvue, { i18next }]], 93 | }, 94 | }); 95 | expect(wrapper.text()).toBe("t: - $t:"); 96 | 97 | await withTimeout(() => { 98 | backend.fakeLoaded("en", "translation", { hello: "Hello world" }); 99 | }); 100 | await initPromise; 101 | await expectText(wrapper, "t: - $t: Hello world"); 102 | 103 | await withTimeout(() => { 104 | backend.fakeLoaded("en", "nsp2", { hello: "Greetings globe!" }); 105 | }); 106 | await expectText(wrapper, "t: Greetings globe! - $t: Hello world"); 107 | }); 108 | -------------------------------------------------------------------------------- /tests/simple.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import { expect, test } from "vitest"; 3 | import globalI18next from "i18next"; 4 | import i18nextvue from "../index"; 5 | import { expectText, TranslateWithKey } from "./helpers"; 6 | 7 | test("Simple translate", async () => { 8 | const i18next = globalI18next.createInstance(); 9 | await i18next.init({ 10 | lng: "en", 11 | resources: { 12 | en: { translation: { hello: "Hello world" } }, 13 | }, 14 | }); 15 | 16 | const wrapper = mount(TranslateWithKey, { 17 | props: { 18 | k: "hello", 19 | }, 20 | global: { 21 | plugins: [[i18nextvue, { i18next }]], 22 | }, 23 | }); 24 | 25 | expect(wrapper.text()).toBe("Hello world"); 26 | }); 27 | 28 | test("Language change", async () => { 29 | const i18next = globalI18next.createInstance(); 30 | await i18next.init({ 31 | lng: "en", 32 | resources: { 33 | en: { translation: { hello: "Hello world" } }, 34 | de: { translation: { hello: "Hallo Welt" } }, 35 | }, 36 | }); 37 | 38 | const wrapper = mount(TranslateWithKey, { 39 | props: { 40 | k: "hello", 41 | }, 42 | global: { 43 | plugins: [[i18nextvue, { i18next }]], 44 | }, 45 | }); 46 | 47 | expect(wrapper.text()).toBe("Hello world"); 48 | 49 | await i18next.changeLanguage("de"); 50 | await expectText(wrapper, "Hallo Welt"); 51 | 52 | await i18next.changeLanguage("en"); 53 | await expectText(wrapper, "Hello world"); 54 | }); 55 | 56 | test("$i18next reactivity", async () => { 57 | const LangComponent = { 58 | template: "{{ $i18next.language }}
", 59 | }; 60 | const i18next = globalI18next.createInstance(); 61 | await i18next.init({ 62 | lng: "en", 63 | resources: {}, 64 | }); 65 | 66 | const wrapper = mount(LangComponent, { 67 | global: { 68 | plugins: [[i18nextvue, { i18next }]], 69 | }, 70 | }); 71 | 72 | expect(wrapper.text()).toBe("en"); 73 | 74 | await i18next.changeLanguage("de"); 75 | await expectText(wrapper, "de"); 76 | 77 | await i18next.changeLanguage("en"); 78 | await expectText(wrapper, "en"); 79 | }); 80 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "declarationDir": "types", 5 | "module": "Node16", 6 | "outDir": "dist", 7 | "target": "ES2017", 8 | "removeComments": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "moduleResolution": "Node16", 12 | "types": ["vitest/globals"] 13 | }, 14 | "include": ["index.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | environment: "jsdom", 7 | coverage: { 8 | reporter: ["text"], 9 | }, 10 | }, 11 | }); 12 | --------------------------------------------------------------------------------