Files
AboutMe/src/utils/language.ts
2026-02-02 22:47:52 +03:00

210 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
type SupportedLanguage,
SUPPORTED_LANGUAGES,
langToTranslateMap,
translateToLangMap,
LANGUAGE_CONFIG,
} from "@i18n/language";
import {
siteConfig,
} from "@/config";
// 重新导出以保持向后兼容
export { SUPPORTED_LANGUAGES, type SupportedLanguage, langToTranslateMap, translateToLangMap };
// 语言存储键
const LANG_STORAGE_KEY = "selected-language";
// 存储语言设置
export function setStoredLanguage(lang: string): void {
if (typeof localStorage !== "undefined") {
localStorage.setItem(LANG_STORAGE_KEY, lang);
}
}
// 获取存储的语言设置
export function getStoredLanguage(): string | null {
if (typeof localStorage !== "undefined") {
return localStorage.getItem(LANG_STORAGE_KEY);
}
return null;
}
// 获取默认语言配置
export function getDefaultLanguage(): string {
const fallback = siteConfig.lang;
if (typeof document !== "undefined") {
const configCarrier = document.getElementById("config-carrier");
return configCarrier?.dataset.lang || fallback;
}
return fallback;
}
// 将配置文件的语言代码转换为翻译服务的语言代码
export function getTranslateLanguageFromConfig(configLang: string): string {
return langToTranslateMap[configLang] || "chinese_simplified";
}
// 获取解析后的站点语言代码
export function getResolvedSiteLang(): SupportedLanguage {
const configLang = getDefaultLanguage() as any;
if (SUPPORTED_LANGUAGES.includes(configLang)) {
return configLang as SupportedLanguage;
}
// 如果 siteConfig.lang 不合规,则使用浏览器检测到的语言
return detectBrowserLanguage();
}
// 将翻译服务的语言代码转换为配置文件的语言代码
export function getConfigLanguageFromTranslate(translateLang: string): string {
return translateToLangMap[translateLang] || "zh";
}
// 获取语言的显示名称
export function getLanguageDisplayName(langCode: string): string {
// 先尝试作为配置语言代码查找
if (langCode in LANGUAGE_CONFIG) {
return LANGUAGE_CONFIG[langCode as SupportedLanguage].displayName;
}
// 尝试作为翻译服务代码查找
const configLang = translateToLangMap[langCode];
if (configLang && configLang in LANGUAGE_CONFIG) {
return LANGUAGE_CONFIG[configLang as SupportedLanguage].displayName;
}
// 如果都找不到,返回原始代码
return langCode;
}
// 检测浏览器语言并返回支持的语言代码
export function detectBrowserLanguage(fallbackLang: SupportedLanguage = "en"): SupportedLanguage {
// 服务端渲染时返回备用语言
if (typeof window === "undefined" || typeof navigator === "undefined") {
return fallbackLang;
}
// 获取浏览器语言列表
const browserLangs = navigator.languages || [navigator.language];
// 遍历浏览器语言列表,找到第一个支持的语言
for (const browserLang of browserLangs) {
// 提取主语言代码(例如:'zh-CN' -> 'zh', 'en-US' -> 'en'
const langCode = browserLang.toLowerCase().split("-")[0];
// 检查是否在支持的语言列表中
if (SUPPORTED_LANGUAGES.includes(langCode as SupportedLanguage)) {
return langCode as SupportedLanguage;
}
}
// 如果没有找到支持的语言,返回备用语言
return fallbackLang;
}
// 获取当前站点语言(优先使用缓存,其次是配置语言,最后是浏览器检测)
export function getSiteLanguage(configLang?: string): string {
// 优先从缓存读取
const storedLang = getStoredLanguage();
if (storedLang) return storedLang;
// 其次使用传入的配置语言或从 carrier 获取的默认语言
const defaultLang = configLang || getDefaultLanguage();
if (SUPPORTED_LANGUAGES.includes(defaultLang as SupportedLanguage)) {
return langToTranslateMap[defaultLang];
}
// 最后自动检测浏览器语言并转换为翻译服务代码
const browserLang = detectBrowserLanguage();
return langToTranslateMap[browserLang];
}
// 初始化翻译功能
export function initTranslateService(): void {
if (typeof window === "undefined" || !siteConfig.translate?.enable) return;
// 检查 translate.js 是否已加载
const translate = (window as any).translate;
if (!translate || (window as any).translateInitialized) return;
// 配置 translate.js
if (siteConfig.translate.service) {
translate.service.use(siteConfig.translate.service);
}
// 设置源语言(始终是网站渲染的语言)
const resolvedLang = getResolvedSiteLang();
const sourceLang = getTranslateLanguageFromConfig(resolvedLang);
translate.language.setLocal(sourceLang);
// 获取目标语言(缓存 -> 配置 -> 浏览器)
const targetLang = getSiteLanguage(resolvedLang);
// 如果目标语言不同于源语言,则设置目标语言
if (targetLang && targetLang !== sourceLang) {
translate.to = targetLang;
}
// 自动识别语言
if (siteConfig.translate.autoDiscriminate) {
translate.setAutoDiscriminateLocalLanguage();
}
// 设置忽略项
if (siteConfig.translate.ignoreClasses) {
siteConfig.translate.ignoreClasses.forEach((className: string) => {
translate.ignore.class.push(className);
});
}
if (siteConfig.translate.ignoreTags) {
siteConfig.translate.ignoreTags.forEach((tagName: string) => {
translate.ignore.tag.push(tagName);
});
}
// UI 配置
if (siteConfig.translate.showSelectTag === false) {
translate.selectLanguageTag.show = false;
}
// 接管存储逻辑:使用自定义缓存并同步到 translate.js
translate.storage.set = function (key: string, value: string) {
if (key === "to") { // translate.js 使用 "to" 存储目标语言
setStoredLanguage(value);
} else {
localStorage.setItem(key, value);
}
};
translate.storage.get = function (key: string) {
if (key === "to") {
return getStoredLanguage();
}
return localStorage.getItem(key);
};
// 启动翻译监听
translate.listener.start();
(window as any).translateInitialized = true;
// 如果目标语言存在且不是源语言,执行翻译
// 强制执行一次 execute 以确保初始化时应用翻译
if (translate.to && translate.to !== translate.language.getLocal()) {
// 延迟一小段时间执行,确保 DOM 完全就绪
setTimeout(() => {
translate.execute();
}, 10);
} else if (translate.to === translate.language.getLocal()) {
// 如果目标语言就是源语言,确保处于未翻译状态
// 有时插件可能会残留之前的翻译状态
translate.reset();
}
}
// 加载并初始化翻译功能
export async function loadAndInitTranslate(): Promise<void> {
if (typeof window === "undefined" || !siteConfig.translate?.enable) return;
try {
// 检查是否已经加载
if (!(window as any).translate) {
// 使用动态导入Vite 会自动处理代码分割
await import("@/plugins/translate");
(window as any).translateScriptLoaded = true;
}
// 初始化服务
initTranslateService();
} catch (error) {
console.error('Failed to load or init translate.js:', error);
}
}
// 切换语言
export function toggleLanguage(langCode: string): void {
const translate = (window as any).translate;
if (!translate) return;
// 切换语言
translate.changeLanguage(langCode);
setStoredLanguage(langCode);
}