1. 架构目标与设计原则 (Architecture Goals & Principles)

在无前端打包构建器(No-Bundler / Vanilla HTML + CDN + UMD)的多页面静态站点(Multi-Page Application, MPA)中,全站国际化(Internationalization, i18n)需要满足以下关键约束:

  1. 零白屏与零闪烁 (Zero-FOUC Early Bootstrap):在首屏渲染瞬间即可确定当前语言,避免先显示中文后闪烁为英文。
  2. 多层级语言优先级判定 (Multi-Tier Language Resolution):
    $$\text{Priority: URL Query Param } (?lang=) \to \text{localStorage} \to \text{Cookie} \to \text{navigator.language} \to \text{Default (zh)}$$
  3. 全栈双向同态 (Full-Stack Symmetry):

    • 前端层 (Frontend):内联全量工具与 UI 字典,挂载全局语言管理器 window.__i18n,无缝驱动 Vue 3 响应式、DOM 静态节点、Element-Plus UMD 国际化包(ElementPlusLocaleEn / ElementPlusLocaleZhTw)。
    • 后端层 (Backend):在 Node.js API 层面读取 Cookie / Header / Query 参数,返回对应语言的错误码(Error Codes)、提示消息(Notice Messages)与 IP 归属地(Geolocation)。

2. 核心架构与各层实现 (Core Implementation)

2.1 全局语言核心引擎 (js/i18n.js)

(function (global) {
  'use strict';

  // 1. 内嵌全量字典 (避免异步网络请求导致的翻译延迟)
  var I18N_DATA = { /* categories, subcategories, tools: { code: { name, desc } } */ };
  var UI_DICT = { /* 通用操作、统计面板、表单占位符、弹窗文案 */ };

  // 2. 语言标准化与侦测
  function normalizeLang(lang) {
    if (!lang) return 'zh';
    lang = String(lang).toLowerCase().trim();
    if (lang === 'en' || lang.startsWith('en-')) return 'en';
    if (lang =<mark> 'zh-tw' || lang </mark>= 'zh-hk' || lang =<mark> 'zh-mo' || lang </mark>= 'tw' || lang === 'hk') return 'zh-TW';
    return 'zh';
  }

  function detectLang() {
    try {
      var query = new URLSearchParams(window.location.search).get('lang');
      if (query) return normalizeLang(query);
    } catch (_) {}
    try {
      var saved = localStorage.getItem('6767_lang') || localStorage.getItem('lang');
      if (saved) return normalizeLang(saved);
    } catch (_) {}
    try {
      var m = document.cookie.match(/(?:^|;\s*)(?:6767_lang|lang)=([^;]+)/);
      if (m) return normalizeLang(decodeURIComponent(m[1]));
    } catch (_) {}
    try {
      if (navigator.language) return normalizeLang(navigator.language);
    } catch (_) {}
    return 'zh';
  }

  var currentLang = detectLang();

  // 3. 语言切换与多层级广播 (Dispatch Event & DOM Re-scan)
  function setLang(newLang, autoReload) {
    newLang = normalizeLang(newLang);
    currentLang = newLang;
    try {
      localStorage.setItem('6767_lang', newLang);
      document.cookie = '6767_lang=' + encodeURIComponent(newLang) + '; path=/; max-age=31536000; SameSite=Lax';
    } catch (_) {}
    
    document.documentElement.lang = newLang =<mark> 'zh-TW' ? 'zh-TW' : (newLang </mark>= 'en' ? 'en' : 'zh-CN');
    syncElementPlusLocale(newLang);
    localizeDom(document, newLang);
    window.dispatchEvent(new CustomEvent('langchange', { detail: { lang: newLang } }));
    if (autoReload) window.location.reload();
  }

  // 4. Element-Plus UMD 语言包联动
  function syncElementPlusLocale(lang) {
    if (global.ElementPlus) {
      if (lang === 'en' && global.ElementPlusLocaleEn) global.ElementPlusLocale = global.ElementPlusLocaleEn;
      else if (lang === 'zh-TW' && global.ElementPlusLocaleZhTw) global.ElementPlusLocale = global.ElementPlusLocaleZhTw;
      else if (global.ElementPlusLocaleZhCn) global.ElementPlusLocale = global.ElementPlusLocaleZhCn;
    }
  }

  global.__i18n = { getLang, setLang, t, translateToolData, localizeDom, mountSwitcher };
})(window);

2.2 后端多语言适配 (server.js)

在服务端拦截所有 API 请求,提取语言上下文:

function getReqLang(req, query) {
  if (query && query.lang) return normalizeLang(query.lang);
  var hdr = req.headers['x-language'] || req.headers['accept-language'];
  var cookies = req.headers['cookie'] || '';
  var m = cookies.match(/(?:^|;\s*)(?:6767_lang|lang)=([^;]+)/);
  if (m) return normalizeLang(decodeURIComponent(m[1]));
  if (hdr) return normalizeLang(hdr);
  return 'zh';
}

3. 关键注意点与避坑指南 (Key Lessons & Common Pitfalls)

  1. UMD 模块命名规范:

    • Element Plus 官方打包规范中,全局 locale 变量名为 ElementPlusLocaleZhCn、ElementPlusLocaleEn、ElementPlusLocaleZhTw。引入脚本时必须在 element-plus/index.js 之后。
  2. 纯前端多页面 DOM 动态响应:

    • 在多页面架构下,页面可能混合了静态 HTML 骨架与 Vue 3 响应式组件。通过 CustomEvent('langchange') 广播,Vue 组件监听事件重算计算属性(Computed Properties),同时 localizeDom() 遍历更新非 Vue 区域(页面 Title、Meta Description、顶部面包屑、走马灯通知)。
  3. 搜索索引跨语言多维度检索:

    • 搜索索引对象(Search Index)需同时包含原名、英语名、繁体中文名、拼音缩写和工具分类,实现跨语种关键词双向模糊搜索(如英文搜 "base64" / "aes" 命中中文卡片,中文搜 "计算器" 命中英文卡片)。
  4. 动态挂载组件的防崩塌防御与缓存穿透 (Defensive Dynamic Inlining & Cache Busting):

    • 痛点现象:当 JS 核心库通过 DOM 动态挂载复杂 UI 交互元素(如 .lang-dropdown-menu 语言选择浮层),若外部样式表因 CDN 或浏览器磁盘缓存(如 Cache-Control: public, max-age=3600)仍处于旧版本时,浏览器会将默认的 <div> 容器作为常规流块级元素呈现,导致未激活语言项以裸文本形式直接铺满页面左上方。
    • 防御三层法:

      1. 内联样式物理级兜底:在动态渲染的 DOM 模板上默认附加 style="display:none;position:absolute;",并通过 JS 显式驱动显隐,脱离对外部未就绪 CSS 规则的硬依赖。
      2. 脚本自闭环样式注入 (Self-Contained Style Injection):在初始化引擎中先向 <head> 注入微型 <style id="i18n-switcher-css">,令顶栏胶囊组与悬浮菜单具备完全自洽的排版与层叠上下文能力。
      3. 全站资源戳同步迭代 (Cache Invalidation):全站多页面模板统一升级静态资源查询参数(如 ?v=20260905),迫使边缘节点与客户端直接重新拉取最新打包样式与脚本。

本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Web