Files
blog/src/i18n/ui.ts
T
wait4xandGLM-5.3 328ab68f18 feat(cms): 后台扩展五件——关于页 markdown 化/社交/头像/favicon/分轨首页
- T1 关于页=markdown 文档:about.yaml intro 扩为多段 markdown(zh/en),
  AboutPage 用 @astrojs/markdown-remark 的 createMarkdownProcessor(进程
  内单例复用)+ set:html 渲染,正文 p/ul/strong/a 对齐站点排版;名片与
  技能卡结构不动
- T2 社交链接单一数据源:home.yaml socials[](label/url/icon)+
  SocialLinks.astro(图标注册表白名单:github/rss/email/bilibili/x,
  未登记 icon 回退纯文字),首页 zh/en 内联硬编码社交块删除
- T3 头像与 favicon 可上传:schema avatar: image()(Astro Image 出 60px
  WebP,未设回退渐变 SVG);home.yaml favicon → BaseLayout link[rel=icon]
  按扩展名给 type,留空回退 /favicon.svg
- T4 首页分轨:PostList perTrack → 阅读轨/技术轨两段各限 6,各自出口
  (阅读→/books/ 技术→/archives/,en 同构);E2E 断言改为两段各≤6+各自
  出口+阅读在前
- T5 后台列表日期:posts summary「{{pubDate}} · {{title}}」、moments
  「{{fields.pubDate}} · {{fields.text}}」+sortable;photos 无 frontmatter
  日期(JSON sidecar)保持分组 · 标题并在文档写明原因
- 顺手修 content-lint 漏报:中文文件名在 HTML 里为 %XX 编码,脚本直接
  拼盘上路径导致 6 条"缺图"误报 → 加 decodeURI
- 清理:删除书封误传遗留的双重嵌套目录文件

车道:op-gitea(5/5,build+E2E 37/37+lint 47 页 0/0/0);收编复验:
build 46 页 + E2E 37/37 连跑(首跑两红为车道并发构建擦 dist 的瞬时态,
复跑绿)

验证: node v26.13.0 + astro 7.4.4 全量构建 46 页 exit 0;E2E 37/37;
      content-lint 0/0/0;books details/命令面板 playwright 实测

Spec: none (后台能力扩展,车道级交付)

Co-authored-by: GLM-5.3
2026-11-02 15:46:41 +08:00

140 lines
9.3 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.
/**
* UI 双语字典:组件静态文案的单一事实源。
* 约定:key 稳定小写蛇形;zh 是源语言(先有),en 跟随;
* 车道协作:op-gitea 扩展本表并串接五组件(Footer/SearchDialog/gallery/moments/about)。
*/
export type Locale = 'zh' | 'en';
const dict = {
skip_to_content: { zh: '跳到正文', en: 'Skip to content' },
nav_posts: { zh: '文章', en: 'Posts' },
nav_gallery: { zh: '图库', en: 'Gallery' },
nav_about: { zh: '关于', en: 'About' },
cmd_reader: { zh: '阅读模式(纯文本)', en: 'Reader mode (plain text)' },
nav_opensource: { zh: '开源', en: 'Projects' }, // en 用短词:390-430px 尾部主题钮免内滚(用户拍板)
nav_books: { zh: '书架', en: 'Books' },
kbd_gs_books: { zh: '书架', en: 'Books' },
// 论文内置阅读页(arXiv 原站 iframe 直嵌,PDF 不入仓)
paper_read_online: { zh: '在线研读全文 PDF', en: 'Read the full PDF online' },
paper_license: { zh: '授权', en: 'License' },
paper_download: { zh: '下载 PDF', en: 'Download PDF' },
paper_fallback_title: { zh: '无法内嵌展示全文,请用浏览器打开', en: 'Inline preview unavailable — open in a browser' },
paper_wechat_hint: { zh: '微信内请点右上角「···」→ 在浏览器打开', en: 'In WeChat, tap ··· (top right) → Open in Browser' },
paper_open_browser: { zh: '在浏览器中打开 PDF →', en: 'Open the PDF in your browser →' },
paper_loading: { zh: '正在加载 PDF…', en: 'Loading PDF…' },
paper_prev_page: { zh: '上一页', en: 'Previous page' },
paper_next_page: { zh: '下一页', en: 'Next page' },
paper_zoom_in: { zh: '放大', en: 'Zoom in' },
paper_zoom_out: { zh: '缩小', en: 'Zoom out' },
paper_page_label: { zh: '页码', en: 'Page' },
// 折叠批(2026-11-02):首页限 6 篇 + 归档/书架按年折叠
home_more_posts: { zh: '全部文章 →', en: 'All posts →' },
unit_posts: { zh: '篇', en: 'posts' },
search_label: { zh: '搜索', en: 'Search' },
search_placeholder: { zh: '搜索文章…(Esc 关闭)', en: 'Search posts… (Esc to close)' },
search_hint: { zh: '索引由 Pagefind 构建期生成 · 静态站零后端', en: 'Index built by Pagefind · zero-backend static site' },
footer_copyright: { zh: '©', en: '©' },
reading_time_min: { zh: '分钟', en: 'min' },
category_reading: { zh: '阅读', en: 'Reading' },
category_tech: { zh: '技术', en: 'Tech' },
category_photo: { zh: '照片', en: 'Photo' },
latest_photos: { zh: '最新影像', en: 'Latest photos' },
enter_gallery: { zh: '进入图库 →', en: 'Gallery →' },
open_source_projects: { zh: '开源 · Projects', en: 'Open Source · Projects' },
self_hosted: { zh: '自托管', en: 'self-hosted' },
prev_post: { zh: '上一篇', en: 'Previous' },
next_post: { zh: '下一篇', en: 'Next' },
ai_translated_note: {
zh: '本文由机器翻译自中文原文,欢迎指正。',
en: 'Machine-translated from the Chinese original. Corrections welcome.',
},
// ===== T2 车道(op-gitea):Footer/SearchDialog/gallery/moments/about 静态文案 =====
// footer_icp 是法定备案标识,英文页照抄不译
search_no_results: { zh: '无结果', en: 'No results' },
footer_icp: { zh: '陕ICP备2026014860号-1', en: '陕ICP备2026014860号-1' },
gallery_title: { zh: '图库', en: 'Gallery' },
gallery_filter_legend: { zh: '分组筛选', en: 'Filter by group' },
gallery_filter_all: { zh: '全部', en: 'All' },
gallery_filter_street: { zh: '街拍', en: 'Street' },
gallery_filter_landscape: { zh: '风景', en: 'Landscape' },
// {n}/{title}/{exif} 为占位符,调用点 .replace() 填值
gallery_count: { zh: '共 {n} 张 · EXIF 构建期自动提取', en: '{n} photos · EXIF extracted at build time' },
gallery_view_aria: { zh: '查看大图:{title}', en: 'View photo: {title}' },
gallery_view_aria_exif: { zh: '({exif})', en: ' ({exif})' },
gallery_lightbox_label: { zh: '照片查看', en: 'Photo viewer' },
gallery_lightbox_close: { zh: '关闭(Esc)', en: 'Close (Esc)' },
gallery_lightbox_prev: { zh: '上一张(←)', en: 'Previous (←)' },
gallery_lightbox_next: { zh: '下一张(→)', en: 'Next (→)' },
gallery_lightbox_original: { zh: '原图', en: 'Original' },
gallery_lightbox_download: { zh: '下载', en: 'Download' },
moments_title: { zh: '瞬间', en: 'Moments' },
moments_note: { zh: '零碎的记录:随手拍、一句话感想、开源动态。', en: 'Fragmented notes: casual shots, one-liners, and open-source updates.' },
about_title: { zh: '关于', en: 'About' },
about_tracks_title: { zh: '兴趣版图', en: 'Interests' },
about_track_reading_name: { zh: '阅读', en: 'Reading' },
about_track_reading_desc: { zh: '文学与戏剧为主,读到哪写到哪。', en: 'Mostly literature and drama; I write as I read.' },
about_track_tech_name: { zh: '技术', en: 'Tech' },
about_track_tech_desc: { zh: 'Linux、容器与自托管,把家里的小机器折腾成顺手的工具。', en: 'Linux, containers, and self-hosting — turning the little machines at home into handy tools.' },
about_track_photo_name: { zh: '摄影', en: 'Photography' },
about_track_photo_desc: { zh: '街拍与山野,随身一台无反,在等光的间隙按快门。', en: 'Street and landscapes with a mirrorless camera, pressing the shutter while waiting for the light.' },
about_build_aria: { zh: '本站构建说明', en: 'Site build notes' },
about_build_title: { zh: '本站如何构建', en: 'How this site is built' },
about_build_desc: {
zh: '页面由 Astro 静态生成,不含运行时框架 JS;内容住在自托管 gitea——本地编辑器与 gitea 网页写的是同一个 git 仓库,push 之后由 homelab 自动构建发布;评论走自托管 Artalk。页脚 built from 一行链到本次构建对应的 commit,构建过程透明可追溯。',
en: 'Pages are statically generated by Astro with no runtime framework JS. Content lives on a self-hosted gitea — my local editor and the gitea web UI write to the same git repository, and each push is built and published automatically by the homelab. Comments run on self-hosted Artalk. The built-from line in the footer links to the commit behind this build, keeping the process transparent and traceable.',
},
// ===== 热力图(Phase 4 T2 车道):文案全部页面层注入,组件保持 locale 无关 =====
heatmap_title: { zh: '开源活动', en: 'Open Source Activity' },
// {n} 为总贡献次数占位符,调用点 .replace() 填值
heatmap_summary: { zh: '最近一年 {n} 次贡献', en: '{n} contributions in the last year' },
heatmap_less: { zh: '少', en: 'Less' },
heatmap_more: { zh: '多', en: 'More' },
heatmap_note: { zh: '数据来自 GitHub · 构建期生成', en: 'via GitHub · generated at build time' },
// ===== B 车道:Footer 极客双页入口(stack/colophon 页内文案各自内联,不入本表) =====
footer_stack: { zh: '装备', en: 'stack' },
footer_colophon: { zh: '制作', en: 'colophon' },
// ===== C 车道:Keyboard 帮助表全量词条(组件 Astro.url 构建期自推断 locale) =====
kbd_title: { zh: '键盘快捷键', en: 'Keyboard shortcuts' },
kbd_palette: { zh: '命令面板', en: 'Command palette' },
kbd_theme_toggle: { zh: '切换明暗', en: 'Toggle light / dark' },
kbd_this_help: { zh: '本帮助', en: 'This help' },
kbd_gh_home: { zh: '首页', en: 'Home' },
kbd_ga_posts: { zh: '文章', en: 'Posts' },
kbd_gg_gallery: { zh: '图库', en: 'Gallery' },
kbd_gm_moments: { zh: '瞬间', en: 'Moments' },
kbd_terminal: { zh: '终端彩蛋', en: 'Terminal easter egg' },
kbd_esc_close: { zh: '关闭浮层', en: 'Close dialogs' },
kbd_hint: { zh: '提示:/terminal 终端彩蛋等你探索', en: 'Tip: the /terminal easter egg awaits' },
// ===== F 车道:访问量小字(Artalk pv,组件内独立计数) =====
pv_viewed: { zh: '人看过', en: 'views' },
lb_hint: { zh: '滚轮缩放 · 双击放大 · Esc 关闭', en: 'Wheel to zoom · Double-click · Esc' },
lb_hint_touch: { zh: '双指缩放 · 双击放大', en: 'Pinch to zoom · Double-tap' },
// ===== 命令面板(Phase 4 T1 车道):动作/导航/分组与键位提示 =====
nav_home: { zh: '首页', en: 'Home' },
nav_moments: { zh: '瞬间', en: 'Moments' },
cmd_palette_label: { zh: '命令面板', en: 'Command palette' },
cmd_group_actions: { zh: '动作', en: 'Actions' },
cmd_group_nav: { zh: '导航', en: 'Navigation' },
cmd_group_search: { zh: '搜索结果', en: 'Search results' },
cmd_toggle_theme: { zh: '切换明暗主题', en: 'Toggle dark mode' },
// 语言动作按当前页面语言命名目标语(zh 页提示切到 English,反之亦然)
cmd_switch_lang: { zh: '切换到 English', en: 'Switch to Chinese' },
cmd_open_gitea: { zh: '打开 gitea 源码仓', en: 'Open the gitea repo' },
cmd_copy_rss: { zh: '复制 RSS 地址', en: 'Copy RSS address' },
cmd_copied: { zh: '已复制 ✓', en: 'Copied ✓' },
cmd_hint: { zh: '↑↓ 选择 · Enter 执行 · Esc 关闭', en: '↑↓ navigate · Enter run · Esc close' },
} as const;
export type UiKey = keyof typeof dict;
/** 取串:locale 缺省 zh;未知 key 原样返回(编译期 tsc 已挡)。 */
export function t(key: UiKey, locale: Locale = 'zh'): string {
return dict[key][locale] ?? dict[key].zh;
}