6.4 KiB
6.4 KiB
2026-08-16
i18n 取语言方式重构
- 项目 i18n 已配置(defaultLocale: 'en', locales: ['en','cn'], prefixDefaultLocale: false)。
- 将
.astro页面里getLocaleFromUrl(Astro.url)改为Astro.currentLocale(Astro 路由解析,更稳健),并补类型兜底(Astro.currentLocale as Locale) ?? DEFAULT_LOCALE。 - 涉及文件:src/layouts/Layout.astro、src/pages/index.astro、src/pages/cn/index.astro。
- src/i18n/utils.ts 的
getLocaleFromUrl纯函数保留(供 .ts 模块或解析任意 URL 使用)。
结论
Astro.currentLocale可替代getLocaleFromUrl(Astro.url),但仅限 .astro 文件;纯 TS / 解析非当前页 URL 仍需自定义函数。
Astro 浏览器语言检测(重要,修正此前误解)
- Astro 确实提供
Astro.preferredLocale/Astro.preferredLocaleList,但它是服务端按请求结合Accept-Language头计算的,仅 SSR 运行时有效。 - 本项目是静态构建 + nginx 托管:build 阶段无访客、无 Accept-Language 头,运行时也无 Node 进程重算,故
preferredLocale在静态产物里是 undefined、preferredLocaleList回退为全部 i18n.locales,无法在运行时检测访客语言。 - 客户端
navigator.language脚本仍是当前静态部署下唯一运行时生效的方案。 - 若要改用
Astro.preferredLocale+Astro.redirect():必须迁移到 SSR(加 adapter、output:'server'、nginx 后跑 Node),且需在 i18n.locales 为cn补codes: ['zh','zh-CN','zh-TW','zh-HK']才能把浏览器zh-CN匹配到cn(当前locales:['en','cn']裸码匹配不到)。
Bug 修复:header 语言切换器不显示
- 根因:
src/layouts/Layout.astro中语言切换器(LOCALES.map)原是#auth-slot的子节点;页脚 auth 脚本fetchMe()未登录返回 null →render(null)用slot.innerHTML整块重写成 Login/Signup,把语言切换器一并抹掉。任何未登录访客都触发,故语言选项永远看不见。 - 修复:(1) 将语言切换器移出
#auth-slot,独立为#lang-switch兄弟节点,脚本重写不再波及;(2) 未登录时不再硬编码/login重写#auth-slot,保留 SSR 的带语言前缀 Login/Signup,顺带修好 /cn 页登录链接丢前缀问题。 - 教训:header 中「语言切换器」与「登录态 UI」必须分属不同容器,避免被 auth 脚本的 innerHTML 重写误伤。
语言切换器改为下拉菜单
- 将 header
#lang-switch从「English / 中文」并排链接改为 Alpine 下拉(x-data="{open:false}"+@click.outside+@keydown.escape.window,绝对定位right-0、z-50,带 chevron 旋转)。选中项font-bold,其余opacity-60。 - 在
src/styles/global.css补[x-cloak]{display:none!important}(原仅 shows/index 各自<style>有,Layout 作为全站模板缺此规则会导致下拉闪烁)。 - 移除原链接间的分隔斜杠
<span class="opacity-25">/</span>。 - 仍保留独立
#lang-switch容器,不被 footer auth 脚本重写。
语言切换器升级:地球图标 + hover 展开
- 把按钮内容从
{LOCALE_NAMES[locale]} + chevron换成地球 SVG 图标(w-[18px] h-[18px],圆+经线+纬线),移除文本标签和 chevron。 - 行为从「click 切换」改为「hover 展开」,Alpine 加
@mouseenter="open=true"+@mouseleave="open=false";click 仍保留(@click="open=!open")作为触屏兜底。 - 关键坑:按钮与下拉之间的
mt-2空隙会让鼠标从按钮移到下拉时mouseleave先触发、面板被收起。改为top-full(紧贴按钮,无外间距),且整个切换器(图标+菜单)作为同一 Alpine 容器,鼠标在内部移动不会触发关闭。 - 加
x-transition做淡入淡出(150ms / 100ms)+py-2给菜单项留呼吸空间。 - 后续补充:按钮在地球图标后追加当前语言名文字
{LOCALE_NAMES[locale]}(normal-case text-[12px] tracking-[0.5px]+gap-1.5),即「图标 + 当前语言」显示。
i18n URL 转换改用 astro:i18n 内置函数
src/i18n/utils.ts的useTranslatedPath内部改为委托astro:i18n的getRelativeLocaleUrl(targetLocale, clean),自动遵循 i18n 配置(prefixDefaultLocale:false → 默认语言无前缀)。- 同文件
getLocaleFromUrl改为委托getLocaleByPath(pathname)(保留 LOCALES 校验 + DEFAULT_LOCALE 兜底,try/catch)。 astro:i18n可在.ts模块(utils.ts)中 import,前端 frontmatter 同样可用;项目 astro ^7.1.6 支持。- 顺手修复首页语言切换器瑕疵:
Layout.astro的currentPath原只剥离语言前缀,未去尾随index.html,导致首页生成/index.html、/cn/index.html;改为额外if 末段==='index.html' 去掉,现首页正确生成/与/cn。currentPath仅用于语言切换器,改动只影响切换器。 - 验证:构建通过(exit 0),产物 dist/index.html 语言切换器 href 为
/与/cn(首页)、/shows与/cn/shows(其他页)。
彻底去掉 useTranslatedPath 包装层
src/i18n/utils.ts:删除useTranslatedPath函数,移除不再使用的getRelativeLocaleUrl导入(仅 useTranslatedPath 引用过)。getLocaleFromUrl仍委托getLocaleByPath。Layout.astro:frontmatter 直接import { getRelativeLocaleUrl } from 'astro:i18n',删除const translatePath = useTranslatedPath(locale);所有调用点(nav 5 项 + 语言切换器getRelativeLocaleUrl(l, currentPath)+ footer 登录/注册/About/Contact)直接改为getRelativeLocaleUrl(locale, path)。行为与原先一致。
Bug 修复:中文首页切换器链接错误(format:'file' 坑)
- 站点
build.format:'file',中文首页路由 /cn/ 产物是dist/cn.html(英文首页是index.html)。构建期Astro.url.pathname取的是产物路径/cn.html。 - 原 currentPath 只剥离
index.html,不处理cn.html,导致中文首页 currentPath=/cn.html,切换器英文项误生成/cn.html(实际是中文首页)、中文项/cn/cn.html。 - 修复:把"去尾随 index.html"改为"去尾随任意 .html"(
_last.endsWith('.html')则去掉末段)。现中英文首页切换器均正确:index.html →///cn;cn.html →///cn。 - 教训:
format:'file'下 Astro.url.pathname 是产物文件名(含 .html),做"当前逻辑路径"推导时必须剥 .html 后缀,不能只认 index.html。