From 3c24da4af8ebdb270137ed1e0e12c463850a80dd Mon Sep 17 00:00:00 2001 From: "Mr.Dat" Date: Fri, 6 Mar 2026 12:45:29 +0700 Subject: [PATCH] refactor: remove i18n dependency and related code - Removed the i18n module and its related functions from the project. - Eliminated the usage of getActiveI18n and related locale handling in various components and stores. - Updated translation handling to use a new instance creation method. - Cleaned up unused imports and code related to language detection and cookie management. - Adjusted components to directly utilize the new translation setup. --- bun.lock | 3 + package.json | 1 + .../locales/en/{en.json => translation.json} | 0 .../locales/vi/{vi.json => translation.json} | 0 src/client.ts | 4 - src/composables/useAppConfirm.ts | 1 - src/composables/useUploadQueue.ts | 1 - src/i18n/index.ts | 19 --- src/index.tsx | 3 +- src/lib/translation/client.ts | 15 --- src/lib/translation/index.ts | 115 +++++++----------- src/lib/utils.ts | 14 +-- src/main.ts | 19 +-- src/routes/home/Home.vue | 6 +- src/routes/settings/pages/Billing.vue | 3 +- .../settings/pages/SecurityNConnected.vue | 10 +- .../components/Detail/VideoInfoHeader.vue | 4 +- src/server/routes/ssr.ts | 24 +--- src/stores/auth.ts | 87 ++++++------- 19 files changed, 114 insertions(+), 215 deletions(-) rename public/locales/en/{en.json => translation.json} (100%) rename public/locales/vi/{vi.json => translation.json} (100%) delete mode 100644 src/i18n/index.ts delete mode 100644 src/lib/translation/client.ts diff --git a/bun.lock b/bun.lock index c6dc6d5..62a08ea 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "": { "name": "holistream", "dependencies": { + "@hono/node-server": "^1.19.11", "@pinia/colada": "^0.21.7", "@unhead/vue": "^2.1.10", "@vueuse/core": "^14.2.1", @@ -169,6 +170,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], + "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], diff --git a/package.json b/package.json index dd2c089..fed71dc 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "tail": "wrangler tail" }, "dependencies": { + "@hono/node-server": "^1.19.11", "@pinia/colada": "^0.21.7", "@unhead/vue": "^2.1.10", "@vueuse/core": "^14.2.1", diff --git a/public/locales/en/en.json b/public/locales/en/translation.json similarity index 100% rename from public/locales/en/en.json rename to public/locales/en/translation.json diff --git a/public/locales/vi/vi.json b/public/locales/vi/translation.json similarity index 100% rename from public/locales/vi/vi.json rename to public/locales/vi/translation.json diff --git a/src/client.ts b/src/client.ts index b7461c4..e528ec8 100644 --- a/src/client.ts +++ b/src/client.ts @@ -13,10 +13,6 @@ async function render() { pinia.use(PiniaSharedState({ enable: true, initialize: true })); hydrateQueryCache(queryCache, appData.$colada || {}); - Object.entries(appData).forEach(([key, value]) => { - (window as any)[key] = value; - }); - await router.isReady(); app.mount('body', true); } diff --git a/src/composables/useAppConfirm.ts b/src/composables/useAppConfirm.ts index 4aee38e..a3c7cb0 100644 --- a/src/composables/useAppConfirm.ts +++ b/src/composables/useAppConfirm.ts @@ -1,5 +1,4 @@ import { computed, reactive, readonly } from 'vue'; -import { getActiveI18n } from '@/i18n'; export type AppConfirmOptions = { message: string; diff --git a/src/composables/useUploadQueue.ts b/src/composables/useUploadQueue.ts index 7913a98..1a719d9 100644 --- a/src/composables/useUploadQueue.ts +++ b/src/composables/useUploadQueue.ts @@ -1,4 +1,3 @@ -import { getActiveI18n } from '@/i18n'; import { computed, ref } from 'vue'; export interface QueueItem { diff --git a/src/i18n/index.ts b/src/i18n/index.ts deleted file mode 100644 index f752a67..0000000 --- a/src/i18n/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { i18n as I18nInstance } from 'i18next'; - -import { getClientI18nInstance } from '@/lib/translation/client'; - -import { defaultLocale, supportedLocales, type SupportedLocale } from './constants'; - -export { supportedLocales, defaultLocale } from './constants'; -export type { SupportedLocale } from './constants'; -export { localeCookieKey } from './constants'; - -export const normalizeLocale = (locale?: string): SupportedLocale => { - if (!locale) return defaultLocale; - const normalized = locale.toLowerCase().split('-')[0] as SupportedLocale; - return supportedLocales.includes(normalized) ? normalized : defaultLocale; -}; - -export const getActiveI18n = (): I18nInstance | undefined => { - return import.meta.env.SSR ? undefined : getClientI18nInstance(); -}; diff --git a/src/index.tsx b/src/index.tsx index c834ce9..71b7eab 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,6 @@ import { Hono } from 'hono'; +import { serveStatic } from "@hono/node-server/serve-static"; import { apiProxyMiddleware } from './server/middlewares/apiProxy'; import { setupMiddlewares } from './server/middlewares/setup'; import { registerDisplayRoutes } from './server/routes/display'; @@ -15,7 +16,7 @@ setupMiddlewares(app); // API proxy middleware (handles /r/*) app.use(apiProxyMiddleware); - +app.use(serveStatic({ root: './public' })) // Routes registerWellKnownRoutes(app); registerMergeRoutes(app); diff --git a/src/lib/translation/client.ts b/src/lib/translation/client.ts deleted file mode 100644 index 10bccb8..0000000 --- a/src/lib/translation/client.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { i18n as I18nInstance } from 'i18next'; - -import { createI18nInstance, initI18nInstance } from '@/lib/translation'; - -let clientI18n: I18nInstance | undefined; - -export const createI18nForClient = async (language?: string) => { - if (!clientI18n) { - clientI18n = createI18nInstance(false); - } - - return initI18nInstance(clientI18n, language, false); -}; - -export const getClientI18nInstance = () => clientI18n; diff --git a/src/lib/translation/index.ts b/src/lib/translation/index.ts index bb26c00..6108563 100644 --- a/src/lib/translation/index.ts +++ b/src/lib/translation/index.ts @@ -1,75 +1,44 @@ -import i18next, { type i18n as I18nInstance } from 'i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; -import I18NextHttpBackend from 'i18next-http-backend'; +import i18next from "i18next"; +import LanguageDetector from "i18next-browser-languagedetector"; +import I18NextHttpBackend, { HttpBackendOptions } from "i18next-http-backend"; +const backendOptions: HttpBackendOptions = { + loadPath: 'http://localhost:5173/locales/{{lng}}/{{ns}}.json', + request: (_options, url, _payload, callback) => { + fetch(url) + .then((res) => + res.json().then((r) => { + callback(null, { + data: JSON.stringify(r), + status: 200, + }) + }) + ) + .catch(() => { + callback(null, { + status: 500, + data: '', + }) + }) + }, +} +export const createI18nInstance = (lng: string) => { + console.log('Initializing i18n with language:', lng); +const i18n = i18next.createInstance(); -import { defaultLocale, localeCookieKey, supportedLocales, type SupportedLocale } from '@/i18n/constants'; - -const runtimeNamespace = 'translation'; - -const normalizeLanguage = (language?: string): SupportedLocale => { - if (!language) return defaultLocale; - const normalized = language.toLowerCase().split('-')[0] as SupportedLocale; - return supportedLocales.includes(normalized) ? normalized : defaultLocale; -}; - -const getLoadPath = () => { - const cdnBase = import.meta.env.VITE_I18N_CDN_BASE_URL?.trim().replace(/\/+$/, ''); - if (cdnBase) { - return `${cdnBase}/locales/{{lng}}/{{lng}}.json`; - } - return '/locales/{{lng}}/{{lng}}.json'; -}; - -export const createI18nInstance = (forServer: boolean) => { - const instance = i18next.createInstance(); - - instance.use(I18NextHttpBackend); - if (!forServer) { - instance.use(LanguageDetector); - } - - return instance; -}; - -export const initI18nInstance = async ( - instance: I18nInstance, - language?: string, - forServer: boolean = import.meta.env.SSR, -) => { - const lng = normalizeLanguage(language); - - if (!instance.isInitialized) { - await instance.init({ - supportedLngs: [...supportedLocales], - fallbackLng: defaultLocale, - load: 'languageOnly', - lng, - ns: [runtimeNamespace], - defaultNS: runtimeNamespace, - fallbackNS: runtimeNamespace, - interpolation: { - escapeValue: false, - }, - backend: { - loadPath: getLoadPath(), - }, - ...(forServer - ? {} - : { - detection: { - order: ['cookie', 'navigator', 'htmlTag'], - lookupCookie: localeCookieKey, - caches: ['cookie'], - }, - }), - }); - - return instance; - } - - if (instance.resolvedLanguage !== lng) { - await instance.changeLanguage(lng); - } - - return instance; +i18n + .use(I18NextHttpBackend) + .use(LanguageDetector) + .init({ + lng, + supportedLngs: ["en", "vi"], + fallbackLng: "en", + defaultNS: "translation", + ns: ['translation'], + interpolation: { + escapeValue: false, + }, + backend: backendOptions, + }); + return i18n; }; +export default createI18nInstance; \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 885d2ac..c310951 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,5 @@ import type { ClassValue } from "clsx"; import { clsx } from "clsx"; -import { getActiveI18n } from '@/i18n'; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { @@ -52,10 +51,10 @@ export function getImageAspectRatio(url: string): Promise { -const getRuntimeLocaleTag = () => { - const locale = getActiveI18n()?.resolvedLanguage; - return locale === 'vi' ? 'vi-VN' : 'en-US'; -}; +// const getRuntimeLocaleTag = () => { +// const locale = getActiveI18n()?.resolvedLanguage; +// return locale === 'vi' ? 'vi-VN' : 'en-US'; +// }; export const formatBytes = (bytes?: number) => { if (!bytes) return '0 B'; @@ -63,7 +62,8 @@ export const formatBytes = (bytes?: number) => { const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); const value = parseFloat((bytes / Math.pow(k, i)).toFixed(2)); - return `${new Intl.NumberFormat(getRuntimeLocaleTag()).format(value)} ${sizes[i]}`; + return `${value} ${sizes[i]}`; + // return `${new Intl.NumberFormat(getRuntimeLocaleTag()).format(value)} ${sizes[i]}`; }; export const formatDuration = (seconds?: number) => { @@ -80,7 +80,7 @@ export const formatDuration = (seconds?: number) => { export const formatDate = (dateString: string = "", dateOnly: boolean = false) => { if (!dateString) return ''; - return new Date(dateString).toLocaleDateString(getRuntimeLocaleTag(), { + return new Date(dateString).toLocaleDateString("en-US", { month: 'short', day: 'numeric', year: 'numeric', diff --git a/src/main.ts b/src/main.ts index 3e71882..781c05e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,13 +5,11 @@ import { createPinia } from 'pinia'; import { createSSRApp } from 'vue'; import { RouterView } from 'vue-router'; -import type { i18n as I18nInstance } from 'i18next'; import I18NextVue from 'i18next-vue'; -import { createI18nInstance, initI18nInstance } from '@/lib/translation'; -import { createI18nForClient } from '@/lib/translation/client'; import { withErrorBoundary } from './lib/hoc/withErrorBoundary'; +import createI18nInstance from './lib/translation'; import createAppRouter from './routes'; const bodyClass = ':uno: font-sans text-gray-800 antialiased flex flex-col min-h-screen'; @@ -21,7 +19,7 @@ const getSerializedAppData = () => { return JSON.parse(document.getElementById('__APP_DATA__')?.innerText || '{}') as Record; }; -export async function createApp(lng: string = 'en', i18next?: I18nInstance) { +export async function createApp(lng: string = 'en') { const pinia = createPinia(); const app = createSSRApp(withErrorBoundary(RouterView)); @@ -35,13 +33,7 @@ export async function createApp(lng: string = 'en', i18next?: I18nInstance) { } }); app.use(pinia); - const runtimeI18n = import.meta.env.SSR - ? (i18next ?? createI18nInstance(true)) - : await createI18nForClient(lng); - if (import.meta.env.SSR) { - await initI18nInstance(runtimeI18n, lng, true); - } - app.use(I18NextVue, { i18next: runtimeI18n }); + app.use(I18NextVue, { i18next: createI18nInstance(lng) }); app.use(PiniaColada, { pinia, plugins: [ @@ -57,8 +49,6 @@ export async function createApp(lng: string = 'en', i18next?: I18nInstance) { }); const queryCache = useQueryCache(); - const router = createAppRouter(); - app.use(router); if (!import.meta.env.SSR) { Object.entries(appData).forEach(([key, value]) => { @@ -69,5 +59,8 @@ export async function createApp(lng: string = 'en', i18next?: I18nInstance) { } } + const router = createAppRouter(); + app.use(router); + return { app, router, head, pinia, bodyClass, queryCache }; } diff --git a/src/routes/home/Home.vue b/src/routes/home/Home.vue index 1e4aac4..1ad081c 100644 --- a/src/routes/home/Home.vue +++ b/src/routes/home/Home.vue @@ -172,12 +172,12 @@