From 2a2b20bdf953672d1e44e7fccf92f8c357fdc42b Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Mon, 20 Oct 2025 18:01:16 +0300 Subject: [PATCH 1/4] fix: use useI18n instead of window.i18n --- adminforth/spa/src/main.ts | 4 ---- adminforth/spa/src/utils.ts | 6 ++++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/adminforth/spa/src/main.ts b/adminforth/spa/src/main.ts index 5f4b830ec..b1ba06492 100644 --- a/adminforth/spa/src/main.ts +++ b/adminforth/spa/src/main.ts @@ -5,7 +5,6 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' -import { initI18n } from './i18n' export const app: ReturnType = createApp(App) /* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */ @@ -13,9 +12,6 @@ export const app: ReturnType = createApp(App) app.use(createPinia()) app.use(router) -// get access to i18n instance outside components -window.i18n = initI18n(app); - /* IMPORTANT:ADMINFORTH CUSTOM USES */ diff --git a/adminforth/spa/src/utils.ts b/adminforth/spa/src/utils.ts index eac865951..cfeaf9dec 100644 --- a/adminforth/spa/src/utils.ts +++ b/adminforth/spa/src/utils.ts @@ -9,10 +9,12 @@ import adminforth from './adminforth'; import sanitizeHtml from 'sanitize-html' import debounce from 'debounce'; import type { AdminForthResourceColumnInputCommon, Predicate } from '@/types/Common'; +import { useI18n } from 'vue-i18n'; const LS_LANG_KEY = `afLanguage`; const MAX_CONSECUTIVE_EMPTY_RESULTS = 2; const ITEMS_PER_PAGE_LIMIT = 100; +const { t } = useI18n(); export async function callApi({path, method, body, headers}: { path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' @@ -42,11 +44,11 @@ export async function callApi({path, method, body, headers}: { // if it is internal error, say to user if (e instanceof TypeError && e.message === 'Failed to fetch') { // this is a network error - adminforth.alert({variant:'danger', message: window.i18n?.global?.t('Network error, please check your Internet connection and try again'),}) + adminforth.alert({variant:'danger', message: t('Network error, please check your Internet connection and try again'),}) return null; } - adminforth.alert({variant:'danger', message: window.i18n?.global?.t('Something went wrong, please try again later'),}) + adminforth.alert({variant:'danger', message: t('Something went wrong, please try again later'),}) console.error(`error in callApi ${path}`, e); } } From 6c66d27875d70265e6428e4a7d4a4a3d5a04ab32 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Tue, 21 Oct 2025 13:25:28 +0300 Subject: [PATCH 2/4] Revert "fix: use useI18n instead of window.i18n" This reverts commit 2a2b20bdf953672d1e44e7fccf92f8c357fdc42b. --- adminforth/spa/src/main.ts | 4 ++++ adminforth/spa/src/utils.ts | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/adminforth/spa/src/main.ts b/adminforth/spa/src/main.ts index b1ba06492..5f4b830ec 100644 --- a/adminforth/spa/src/main.ts +++ b/adminforth/spa/src/main.ts @@ -5,6 +5,7 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' +import { initI18n } from './i18n' export const app: ReturnType = createApp(App) /* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */ @@ -12,6 +13,9 @@ export const app: ReturnType = createApp(App) app.use(createPinia()) app.use(router) +// get access to i18n instance outside components +window.i18n = initI18n(app); + /* IMPORTANT:ADMINFORTH CUSTOM USES */ diff --git a/adminforth/spa/src/utils.ts b/adminforth/spa/src/utils.ts index cfeaf9dec..eac865951 100644 --- a/adminforth/spa/src/utils.ts +++ b/adminforth/spa/src/utils.ts @@ -9,12 +9,10 @@ import adminforth from './adminforth'; import sanitizeHtml from 'sanitize-html' import debounce from 'debounce'; import type { AdminForthResourceColumnInputCommon, Predicate } from '@/types/Common'; -import { useI18n } from 'vue-i18n'; const LS_LANG_KEY = `afLanguage`; const MAX_CONSECUTIVE_EMPTY_RESULTS = 2; const ITEMS_PER_PAGE_LIMIT = 100; -const { t } = useI18n(); export async function callApi({path, method, body, headers}: { path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' @@ -44,11 +42,11 @@ export async function callApi({path, method, body, headers}: { // if it is internal error, say to user if (e instanceof TypeError && e.message === 'Failed to fetch') { // this is a network error - adminforth.alert({variant:'danger', message: t('Network error, please check your Internet connection and try again'),}) + adminforth.alert({variant:'danger', message: window.i18n?.global?.t('Network error, please check your Internet connection and try again'),}) return null; } - adminforth.alert({variant:'danger', message: t('Something went wrong, please try again later'),}) + adminforth.alert({variant:'danger', message: window.i18n?.global?.t('Something went wrong, please try again later'),}) console.error(`error in callApi ${path}`, e); } } From 8085af112b34b6dfe3f4608eabcd3c7bfccef13c Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Tue, 21 Oct 2025 13:27:03 +0300 Subject: [PATCH 3/4] fix: declare global interface for i18n on window object --- adminforth/spa/src/main.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/adminforth/spa/src/main.ts b/adminforth/spa/src/main.ts index 5f4b830ec..e2897bdba 100644 --- a/adminforth/spa/src/main.ts +++ b/adminforth/spa/src/main.ts @@ -6,6 +6,11 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' import { initI18n } from './i18n' +declare global { + interface Window { + i18n: any; + } +} export const app: ReturnType = createApp(App) /* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */ From bd8b122df0fbbe7beaa3686c4252a20fa2ae70f5 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Thu, 23 Oct 2025 17:35:26 +0300 Subject: [PATCH 4/4] fix: add translations to the callApi error messages --- adminforth/spa/package.json | 2 +- adminforth/spa/src/i18n.ts | 6 ++++-- adminforth/spa/src/main.ts | 7 +------ adminforth/spa/src/utils.ts | 6 ++++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/adminforth/spa/package.json b/adminforth/spa/package.json index 4dd52b775..231060c1b 100644 --- a/adminforth/spa/package.json +++ b/adminforth/spa/package.json @@ -10,7 +10,7 @@ "build-only": "vite build", "type-check": "vue-tsc --build --force", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", - "i18n:extract": "echo {} > i18n-empty.json && vue-i18n-extract report --vueFiles \"./src/**/*.{js,vue}\" --output ./i18n-messages.json --languageFiles \"i18n-empty.json\" --add" + "i18n:extract": "echo {} > i18n-empty.json && vue-i18n-extract report --vueFiles \"./src/**/*.{js,vue,ts}\" --output ./i18n-messages.json --languageFiles \"i18n-empty.json\" --add" }, "dependencies": { "@iconify-prerendered/vue-flag": "^0.28.1748584105", diff --git a/adminforth/spa/src/i18n.ts b/adminforth/spa/src/i18n.ts index 7dd6a821d..c59321d0b 100644 --- a/adminforth/spa/src/i18n.ts +++ b/adminforth/spa/src/i18n.ts @@ -7,7 +7,7 @@ function slavicPluralRule(choice: number, choicesLength: number, orgRule: any) { if (choice === 0) { return 0 } - + const teen = choice > 10 && choice < 20 const endsWithOne = choice % 10 === 1 @@ -21,6 +21,8 @@ function slavicPluralRule(choice: number, choicesLength: number, orgRule: any) { return choicesLength < 4 ? 2 : 3 } +export let i18nInstance: ReturnType | null = null + export function initI18n(app: ReturnType) { const i18n = createI18n({ legacy: false, @@ -48,7 +50,7 @@ export function initI18n(app: ReturnType) { return key + ' '; }, }); - app.use(i18n); + i18nInstance = i18n return i18n } \ No newline at end of file diff --git a/adminforth/spa/src/main.ts b/adminforth/spa/src/main.ts index e2897bdba..ba80262ae 100644 --- a/adminforth/spa/src/main.ts +++ b/adminforth/spa/src/main.ts @@ -6,11 +6,6 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' import { initI18n } from './i18n' -declare global { - interface Window { - i18n: any; - } -} export const app: ReturnType = createApp(App) /* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */ @@ -19,7 +14,7 @@ app.use(createPinia()) app.use(router) // get access to i18n instance outside components -window.i18n = initI18n(app); +initI18n(app); /* IMPORTANT:ADMINFORTH CUSTOM USES */ diff --git a/adminforth/spa/src/utils.ts b/adminforth/spa/src/utils.ts index eac865951..9ce618649 100644 --- a/adminforth/spa/src/utils.ts +++ b/adminforth/spa/src/utils.ts @@ -9,6 +9,7 @@ import adminforth from './adminforth'; import sanitizeHtml from 'sanitize-html' import debounce from 'debounce'; import type { AdminForthResourceColumnInputCommon, Predicate } from '@/types/Common'; +import { i18nInstance } from './i18n' const LS_LANG_KEY = `afLanguage`; const MAX_CONSECUTIVE_EMPTY_RESULTS = 2; @@ -19,6 +20,7 @@ export async function callApi({path, method, body, headers}: { body?: any headers?: Record }): Promise { + const t = i18nInstance?.global.t || ((s: string) => s) const options = { method, headers: { @@ -42,11 +44,11 @@ export async function callApi({path, method, body, headers}: { // if it is internal error, say to user if (e instanceof TypeError && e.message === 'Failed to fetch') { // this is a network error - adminforth.alert({variant:'danger', message: window.i18n?.global?.t('Network error, please check your Internet connection and try again'),}) + adminforth.alert({variant:'danger', message: t('Network error, please check your Internet connection and try again'),}) return null; } - adminforth.alert({variant:'danger', message: window.i18n?.global?.t('Something went wrong, please try again later'),}) + adminforth.alert({variant:'danger', message: t('Something went wrong, please try again later'),}) console.error(`error in callApi ${path}`, e); } }