119 lines
3.6 KiB
TypeScript
119 lines
3.6 KiB
TypeScript
/**
|
||
* 按路由加载页面级翻译到 page 命名空间
|
||
* 路由 /dice/lottery_pool_config/index -> 加载 langs/{locale}/dice/lottery_pool_config.json
|
||
*/
|
||
import i18n from '@/locales'
|
||
import { LanguageEnum } from '@/enums/appEnum'
|
||
|
||
/** 路由 path 到 locale 文件路径的映射(无前导斜杠、无 /index) */
|
||
export function getPageLocalePath(routePath: string): string | null {
|
||
if (!routePath || routePath === '/') return null
|
||
const normalized = routePath.replace(/\?.*$/, '').replace(/#.*$/, '').replace(/\/$/, '')
|
||
const withoutIndex = normalized.replace(/\/index$/i, '') || '/'
|
||
const path = withoutIndex.replace(/^\//, '') || ''
|
||
if (!path) return null
|
||
return path
|
||
}
|
||
|
||
type PageLocaleModule = { default: Record<string, unknown> }
|
||
|
||
const enModules = import.meta.glob<PageLocaleModule>('./langs/en/**/*.json')
|
||
const zhModules = import.meta.glob<PageLocaleModule>('./langs/zh/**/*.json')
|
||
|
||
function getModuleKey(locale: string, path: string): string {
|
||
return `./langs/${locale}/${path}.json`
|
||
}
|
||
|
||
let lastLoadedPath: string | null = null
|
||
let lastLoadedLocale: string | null = null
|
||
|
||
/** 获取当前语言(locale 可能是 string 或 Ref) */
|
||
function getCurrentLocale(): string {
|
||
const loc = i18n.global.locale
|
||
return typeof loc === 'string' ? loc : (loc as { value: string }).value
|
||
}
|
||
|
||
/**
|
||
* 加载并合并页面翻译到 i18n 的 page 命名空间
|
||
*/
|
||
export async function loadPageLocale(routePath: string): Promise<void> {
|
||
const path = getPageLocalePath(routePath)
|
||
if (!path) {
|
||
clearPageLocale()
|
||
return
|
||
}
|
||
const locale = getCurrentLocale()
|
||
const modules = locale === LanguageEnum.EN ? enModules : zhModules
|
||
|
||
const tryPaths: string[] = [path]
|
||
// 兼容别名路由:菜单 path 为短名但 locale 文件位于模块子目录
|
||
// 例如:/user -> system/user,/game -> dice/game
|
||
if (!path.includes('/')) {
|
||
tryPaths.push(`system/${path}`)
|
||
// 兜底:在任意一级子目录下查找同名文件
|
||
const suffix = `/${path}.json`
|
||
const localePrefix = `./langs/${locale}/`
|
||
for (const key of Object.keys(modules)) {
|
||
if (key.startsWith(localePrefix) && key.endsWith(suffix)) {
|
||
const candidate = key.slice(localePrefix.length, -'.json'.length)
|
||
if (!tryPaths.includes(candidate)) {
|
||
tryPaths.push(candidate)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
let matchedPath: string | null = null
|
||
let loader: (() => Promise<PageLocaleModule>) | undefined
|
||
for (const p of tryPaths) {
|
||
const key = getModuleKey(locale, p)
|
||
const l = modules[key]
|
||
if (l) {
|
||
matchedPath = p
|
||
loader = l
|
||
break
|
||
}
|
||
}
|
||
if (!loader) {
|
||
clearPageLocale()
|
||
return
|
||
}
|
||
if (lastLoadedPath === matchedPath && lastLoadedLocale === locale) {
|
||
return
|
||
}
|
||
try {
|
||
const mod = await loader()
|
||
const message = mod?.default
|
||
if (message && typeof message === 'object') {
|
||
i18n.global.mergeLocaleMessage(locale, { page: message })
|
||
lastLoadedPath = matchedPath
|
||
lastLoadedLocale = locale
|
||
}
|
||
} catch {
|
||
clearPageLocale()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清除 page 命名空间(进入无页面翻译的路由时调用)
|
||
*/
|
||
export function clearPageLocale(): void {
|
||
const locale = getCurrentLocale()
|
||
const messages = i18n.global.getLocaleMessage(locale) as Record<string, unknown>
|
||
if (messages && 'page' in messages) {
|
||
const next = { ...messages }
|
||
delete next.page
|
||
i18n.global.setLocaleMessage(locale, next)
|
||
}
|
||
lastLoadedPath = null
|
||
lastLoadedLocale = null
|
||
}
|
||
|
||
/**
|
||
* 语言切换时清空缓存,下次进入页面会重新加载
|
||
*/
|
||
export function invalidatePageLocaleCache(): void {
|
||
lastLoadedPath = null
|
||
lastLoadedLocale = null
|
||
}
|