110 lines
3.1 KiB
TypeScript
110 lines
3.1 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import { useParams, useRouter } from 'next/navigation';
|
||
|
||
type Locale = 'zh-CN' | 'zh-TW' | 'en';
|
||
|
||
interface TranslationData {
|
||
[key: string]: any;
|
||
}
|
||
|
||
const translations: Record<Locale, Record<string, TranslationData>> = {
|
||
'zh-CN': {},
|
||
'zh-TW': {},
|
||
en: {},
|
||
};
|
||
|
||
// Load translation files dynamically
|
||
const loadTranslations = async (locale: Locale, namespace: string): Promise<TranslationData> => {
|
||
if (translations[locale][namespace]) {
|
||
return translations[locale][namespace];
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`/locales/${locale}/${namespace}.json`);
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP ${response.status}`);
|
||
}
|
||
const data = await response.json();
|
||
translations[locale][namespace] = data;
|
||
return data;
|
||
} catch (error) {
|
||
console.error(`Failed to load translation: ${locale}/${namespace}`, error);
|
||
return {};
|
||
}
|
||
};
|
||
|
||
export const useTranslation = (namespace: string = 'common') => {
|
||
const params = useParams();
|
||
const router = useRouter();
|
||
const urlLocale = params?.locale as Locale;
|
||
|
||
const [locale, setLocale] = useState<Locale>(urlLocale || 'zh-CN');
|
||
const [translations, setTranslations] = useState<TranslationData>({});
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
// 如果URL中有locale参数,优先使用URL中的语言
|
||
if (urlLocale && urlLocale !== locale) {
|
||
setLocale(urlLocale);
|
||
}
|
||
}, [urlLocale, locale]);
|
||
|
||
useEffect(() => {
|
||
const loadData = async () => {
|
||
setLoading(true);
|
||
const data = await loadTranslations(locale, namespace);
|
||
setTranslations(data);
|
||
setLoading(false);
|
||
};
|
||
|
||
loadData();
|
||
}, [locale, namespace]);
|
||
|
||
const changeLocale = (newLocale: Locale) => {
|
||
// 更新localStorage
|
||
localStorage.setItem('language', newLocale);
|
||
|
||
// 构建新的URL路径
|
||
const currentPath = window.location.pathname;
|
||
const pathWithoutLocale = currentPath.replace(/^\/[^\/]+/, '');
|
||
const newPath = `/${newLocale}${pathWithoutLocale}`;
|
||
|
||
// 导航到新的语言路径
|
||
router.push(newPath);
|
||
};
|
||
|
||
const t = (key: string, defaultValue?: string): string => {
|
||
const keys = key.split('.');
|
||
let value = translations;
|
||
|
||
for (const k of keys) {
|
||
if (value && typeof value === 'object' && k in value) {
|
||
value = value[k];
|
||
} else {
|
||
return defaultValue || key;
|
||
}
|
||
}
|
||
|
||
return typeof value === 'string' ? value : defaultValue || key;
|
||
};
|
||
|
||
return {
|
||
t,
|
||
locale,
|
||
setLocale: changeLocale,
|
||
loading,
|
||
};
|
||
};
|
||
|
||
// 语言切换工具函数
|
||
export const getLocalizedPath = (path: string, locale: Locale): string => {
|
||
return `/${locale}${path}`;
|
||
};
|
||
|
||
// 验证locale是否有效
|
||
export const isValidLocale = (locale: string): locale is Locale => {
|
||
return ['zh-CN', 'zh-TW', 'en'].includes(locale);
|
||
};
|
||
|
||
export type { Locale };
|