给横幅加一行中文翻译

1674 字
8 分钟
给横幅加一行中文翻译

博客横幅挂了一串英文挺久了。主标题 “With You, My Lord.”,下面副标题六句英文数组,配打字机循环打字。功能上没毛病,但中文读者扫一眼过去,多少隔了一层。

想在这底下垫一行小字中文翻译。本来以为就是多写几个字符串的事,结果牵扯出一连串——打字机怎么同步、移动端字号倒置、断点要逐档补。这一篇记录一下。

先摸清现状#

横幅文字配置在 src/config/backgroundWallpaper.tshomeText 块:

homeText: {
enable: true,
title: "With You, My Lord.",
subtitle: [
"Through Whispering Winds, I Follow",
"Beneath Verdant Leaves, I Await",
// ...共 6 条
],
typewriter: { enable: true, speed: 100, deleteSpeed: 50, pauseTime: 2000 },
}

副标题是数组,打字机开着就循环:打一句 → 暂停 → 删完 → 下一句。渲染走 TypewriterText.astro,主标题和副标题两层结构。

要加翻译,得先想清楚三件事:加在哪一层、数组怎么对应、打字机循环时翻译跟不跟着动。这三点直接决定了实现复杂度——主标题单条好办,副标题数组要么固定一条、要么六条一一对应同步切换,打字机模式下还要处理”英文打完翻译才出现、英文开始删翻译先消失”的时序。

schema 先扩好#

类型定义在 src/types/backgroundWallpaper.ts。照着原有字段加三个可选项:

titleTranslation?: string; // 主标题翻译,单条
subtitleTranslation?: string | string[]; // 副标题翻译,和 subtitle 一一对应
translationSize?: string; // 翻译字号,默认 1rem

三个都可选,不配就什么都不渲染,老配置完全不受影响。subtitleTranslation 设计成和 subtitle 同结构——配数组就要求长度对齐,打字机切到第几句就显示第几句的翻译。

打字机怎么跟着切换#

整个折腾最绕的一环。

打字机组件 TypewriterText.astro 原本只管一段文本:逐字打出、暂停、逐字删除、切下一条。现在要让翻译跟着英文节奏走——英文打完了,翻译淡入;英文开始删了,翻译先藏掉。

思路是给组件加一个 syncText prop,渲染一个同步用的 <span class="typewriter-sync"> 紧跟在主文本后面。组件内部的 TypewriterEffect 类多管一个同步元素:

private showSync() {
if (this.syncElement && this.syncTexts.length > 0) {
this.syncElement.textContent = this.syncTexts[this.currentTextIndex] || "";
}
}
private clearSync() {
if (this.syncElement) this.syncElement.textContent = "";
}

关键是调用时机。原版的 type() 方法在打字完成时直接进暂停、然后开始删除。改完之后,完成那一刻先 showSync() 显示翻译,再进暂停;暂停结束要开始删除前,先 clearSync() 把翻译藏掉,再进删除流程:

} else {
// 打字完成,显示同步翻译
this.showSync();
if (this.texts.length > 1) {
this.isDeleting = true;
this.timeoutId = window.setTimeout(() => {
this.clearSync(); // 删除前先藏翻译
this.type();
}, this.pauseTime);
}
}

顺序不能反。如果翻译在删除过程中还挂着,英文已经少了一半、翻译还完整显示着,画面就很怪——像是翻译在替英文收尾。

非打字机模式(typewriter.enable: false)简单些,副标题每次刷新随机挑一条。这时候翻译用同一个随机索引去取,保证英文和翻译对得上:

const getRandomSubtitle = () => {
const subtitle = homeText?.subtitle;
const translation = homeText?.subtitleTranslation;
if (Array.isArray(subtitle)) {
const i = Math.floor(Math.random() * subtitle.length);
const sub = subtitle[i];
const trans = Array.isArray(translation)
? (translation[i] || translation[0] || "")
: (translation || "");
return { subtitle: sub, translation: trans };
}
// ...
};

主标题翻译直接渲染#

主标题没打字机,就一行字。翻译直接在下面渲染一个 div,字号走 translationSize

{backgroundWallpaper.common?.homeText?.titleTranslation && (
<div class="banner-title-translation text-white/70"
style={{ fontSize: homeText.translationSize || "1rem" }}>
{homeText.titleTranslation}
</div>
)}

副标题这边分两条路:打字机模式把 syncText 传给 TypewriterText;非打字机模式渲染一个 .banner-subtitle-translation span,内容共用 getRandomSubtitle 的结果。还有一段老代码 setRandomSubtitle 内联脚本负责 Swup 切页后重新挑一条——这部分本来就有,只是要让它同步更新翻译 span 的内容。

加粗一下#

翻译默认字重 400,在壁纸上有点飘。主标题是 font-bold(700),翻译不能也用 700 那么重,但 400 太轻了。在 src/styles/banner-title.css 给主标题翻译加了一句:

.banner-title-translation {
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
font-weight: 700;
}

副标题翻译没加粗——副标题本身就没加粗,翻译跟着副标题风格走,保持层级一致。text-shadow 是必须的,不然浅色壁纸上一片白糊。

移动端翻车了#

桌面端跑起来挺正常,主标题英文 + 翻译、副标题打字机 + 同步翻译,节奏也对。切到手机竖屏一看——翻译比英文还大。

原因是主标题和副标题在 layout-styles.css 里有四个移动端断点逐档缩放:

断点主标题副标题
≤480pxmin(4.5rem, 10vw) ≈ 16px1rem
481-640pxmin(4.5rem, 10vw)1.125rem
641-767pxmin(4.5rem, 8vw)1.25rem
768-1023pxmin(4.5rem, 7vw)1.5rem

但翻译的字号来自内联 translationSize(配的 1.125rem),固定值,不走断点。结果在 ≤480px 这种小屏上,副标题缩到了 1rem,翻译还是 1.125rem——翻译比英文大了一圈,层级完全反了。

修复就是老老实实去四个断点里补翻译的字号,用 !important 盖掉内联:

@media (max-width: 480px) {
.banner-title-translation { font-size: 0.85rem !important; }
.banner-subtitle-translation, .typewriter-sync { font-size: 0.8rem !important; }
}
@media (min-width: 481px) and (max-width: 640px) {
.banner-title-translation { font-size: 0.95rem !important; }
.banner-subtitle-translation, .typewriter-sync { font-size: 0.9rem !important; }
}
/* ...另外两档同理 */

注意 .typewriter-sync 也要一起覆盖——打字机模式的翻译走的是这个 span,不跟 .banner-subtitle-translation 走。漏掉它的话,打字机模式在小屏上还是会翻车。

四档字号顺着副标题的缩放梯度调,翻译始终比同级行小一档:

断点主标题翻译副标题翻译副标题(参考)
≤480px0.85rem0.8rem1rem
481-640px0.95rem0.9rem1.125rem
641-767px1rem0.95rem1.25rem
768-1023px1.05rem1.05rem1.5rem
≥1024px1.125rem(内联)1.125rem(内联)1.5rem

收尾#

改动最后落在五个文件:schema 类型定义、TypewriterText 组件、MainGridLayout 渲染、banner-title.css 加粗、layout-styles.css 响应式断点。配置文件里填了中文翻译示例,后来又自己润色了一版措辞,把打字机 pauseTime 从 2000 调到 5000——英文打完留着翻译多看几秒。

整个过程最花心思的不是写代码,是打字机同步时序那段——完成显示、删除前隐藏,顺序反了画面就乱;以及移动端字号倒置这个坑,桌面端完全看不出来,必须真去竖屏看一眼才发现。配置驱动的项目加字段容易,难的是让新字段在所有已有的响应式、动画、切页逻辑里都行为正确。

代码已经提交进 Firefly 仓库。想加翻译的话,在 homeText 里填 titleTranslationsubtitleTranslation 就行,不填完全不影响原有行为。

折腾告一段落。下一篇见。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
给横幅加一行中文翻译
https://kokkoro.me/posts/banner-bilingual-translation/
作者
Kokkoro
发布于
2026-08-13
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
给博客换一只鼠标
博客折腾给 Firefly 博客换一套自定义 Windows 光标:从素材格式的坑(ani/png/cur)、配置驱动接入、动态注入 CSS,到 Swup 切页丢失样式、右键菜单露馅和图标需重新生成等折腾记录。
2
光标尾迹:把 Wallpaper Engine 的花瓣搬到博客上
博客折腾把 Wallpaper Engine 里 Cherry Blossoms 2 的动态花瓣效果搬进 Astro 博客:从读 JSON 物理参数、对齐坐标系与力公式,到五瓣花形相位、运行时染色和加色发光,以及接进 Firefly 的配置过程。
3
证书等了 40 分钟:用阿里云 ESA Pages 部署个人博客的完整实录
博客折腾把 Astro 博客从 GitHub 部署到阿里云 ESA Pages 并绑定新域名的完整实录,重点记录证书卡在 40 分钟背后的 _dnsauth CNAME。
4
如果不管模型会不会胡说八道,大模型理论上能拥有无限上下文吗?
AI日常显存、算力、框架、API 全部不设限,大模型能不能一直往下吃 Token?本文从 O(N²) Attention、KV Cache、Prefill / Decode、FlashAttention 与 PagedAttention 讲到 RoPE 长度外推,最后给出一个更值得追问的方向:长期 AI 需要的也许不是更大的桌子,而是书架和记忆系统。
5
为什么 128K 模型塞不进第 128001 个 Token?真正拦住它的可能根本不是模型
AI日常标称 128K 的模型到了第 128001 个 Token 常常不是「开始胡说」,而是直接拒绝。本文区分 Effective Context Limit 与 Hard Context Limit,从位置编码、RoPE、模型配置、vLLM 的 max_model_len 一路看到 KV Cache 与 GPU Kernel——拦住那个 Token 的通常是整套推理系统。
随机文章随机推荐
Kokkoro
就让身为引导者的我,全力支持主人吧(OxO)
公告
欢迎来到我的博客!希望这里的内容能够帮助到你~
分类
标签
最新动态
站点统计
文章
45
分类
5
标签
115
总字数
125,992
运行时长
0
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.15.6
文章许可
CC BY-NC-SA 4.0