Initial commit

This commit is contained in:
2026-02-02 22:47:52 +03:00
committed by GitHub
commit f53016aeda
239 changed files with 84360 additions and 0 deletions

210
src/utils/language.ts Normal file
View File

@@ -0,0 +1,210 @@
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);
}