04 | 中英文双语的工程实现

发布时间:2026/7/1 9:53:46

04 | 中英文双语的工程实现 本文目录 第 4 篇中英文双语的工程实现 1. 整体设计 2. CJK 字体自动检测2.1 问题2.2 检测方法2.3 字体切换 统一绘制入口 3. 分段缩放 —— 中文为什么需要更大字号3.1 原理3.2 CJKScale 实现 4. 字形码点采集 —— 为什么会显示 ??4.1 问题复现4.2 修复 5. 语言切换与持久化️ 6. 中文全链路修复一览 下篇预告 系列目录系列第 4 篇· 一款游戏只有英文在国内平台发布是不完整的。本文记录加入简体中文支持的工程实践。上一篇222 个 Bug 修复教会我的事 第 4 篇中英文双语的工程实现 1. 整体设计C 语言做 i18n 没有标准做法。最朴素的方案枚举 二维数组。#defineLANG_EN0#defineLANG_ZH1typedefenum{STR_BEGINNER,STR_INTERMEDIATE,STR_EXPERT,STR_TITLE,STR_SUBTITLE,STR_STATISTICS,STR_SETTINGS_HELP,// ... ≈ 100 个 IDSTR_COUNT}StringId;staticconstchar*I18N[LANG_COUNT][STR_COUNT]{[LANG_EN]{[STR_BEGINNER]Beginner,[STR_TITLE]MINESWEEPER,},[LANG_ZH]{[STR_BEGINNER]u8初级,[STR_TITLE]u8扫雷,}};#defineS(id)I18N[currentLang][id]使用DrawT(gameFont,S(STR_STATISTICS),pos,48,2,WHITE);// 英文 → STATISTICS 中文 → 统计数据方案优点缺点✅枚举数组本方案零依赖 · 编译期检查加语言需改源码❌ GNU gettext外部 .po 文件Windows 兼容差❌ JSON/YAML外部编辑友好需引入解析器 100 条字符串 × 2 种语言枚举数组是最实用的选择。 2. CJK 字体自动检测2.1 问题Raylib 的DrawTextEx一次只能用一种字体。英文用font2.ttf漂亮中文需要font4.ttf含 CJK 字符。如果全用 CJK 字体英文字形靠 fallback 渲染风格不统一全用英文字体中文显示为方块。2.2 检测方法CJK 统一码区U4E00–U9FFF全落在 UTF-8 三字节编码范围0xE0开头。检测只需staticboolHasCJK(constchar*text){constunsignedchar*p(constunsignedchar*)text;while(*p){if((*p0xF0)0xE0)returntrue;// 三字节 CJK// 根据 UTF-8 编码前进}returnfalse;}不需要查码点表。O(N) 且 N 30字符串长度帧率影响可忽略。2.3 字体切换 统一绘制入口staticFontPickFont(constchar*text){if(HasCJK(text)cjkFont.texture.id!GetFontDefault().texture.id)returncjkFont;returntitleFont;}staticvoidDrawT(Font dummy,constchar*text,Vector2 pos,floatfontSize,floatspacing,Color tint){(void)dummy;// 兼容旧签名floatfsIsCJKFont(text)?CJKScale(fontSize):fontSize;DrawTextEx(PickFont(text),text,pos,fs,spacing,tint);}️英文 vs 中文渲染对比 3. 分段缩放 —— 中文为什么需要更大字号3.1 原理英文字母 e2 画 中文汉字 赢17 画16×16 像素里2 画和 17 画的辨识度完全不同。3.2 CJKScale 实现staticfloatCJKScale(floatfontSize){if(fontSize14)returnfontSize*1.45f;// 14→≈20pxif(fontSize18)returnfontSize*1.40f;// 16→≈22pxif(fontSize24)returnfontSize*1.35f;// 22→≈30pxif(fontSize36)returnfontSize*1.25f;// 32→≈40pxreturnfontSize*1.20f;// 大字号微调}用途原字号英文 ≈CJK 缩放后提升菜单最佳时间14px小20px✅ 可读设置标签16px一般22px✅ 清楚按钮文字22px正常30px✅ 舒服标题52px大62px✅ 几乎无感⚠️注意MeasureT测量函数也必须使用CJKScale否则居中公式用未缩放尺寸文字画偏了。 4. 字形码点采集 —— 为什么会显示??4.1 问题复现Bug 现象与修复修复前 [ English ] [ ?? ] ← 简体中文 显示为 ?? 修复后 [ English ] [简体中文] ← 正常显示根因与修复过程见 Bug #5 章节。Raylib 的LoadFontEx需要指定要加载哪些 Unicode 码点cjkFontLoadFontEx(font4.ttf,192,codepoints,cpCount);// 不指定的字符 → 纹理里没有 → 渲染为 ?BuildCJKCodepoints最初只扫描I18N[LANG_ZH]翻译表。但设置页的语言按钮文字是硬编码的constchar*langNames[]{English,u8简体中文};// ❌ 不在翻译表简 · 体 · 文 三个字从未出现在翻译表中 → 码点集没有 → 字形缺失 →??。4.2 修复staticintBuildCJKCodepoints(int*codepoints,intmaxCount){// ASCII 总是包含for(inti32;i126;i)codepoints[count]i;// 额外硬编码字符串staticconstchar*extra[]{u8简体中文,NULL};for(intpass0;pass2;pass){constchar**src(pass0)?I18N[LANG_ZH]:extra;// 扫描 UTF-8 序列提取码点去重}returncount;}教训BuildCJKCodepoints必须覆盖所有可能显示在屏幕上的 CJK 字符包括不是从翻译表取的字符串。 5. 语言切换与持久化// 设置页点击切换if(点击简体中文){currentLangLANG_ZH;SaveSettings();}// 启动时恢复intsavedLangLoadSettings();if(savedLangLANG_EN||savedLangLANG_ZH)currentLangsavedLang;语言选择通过settings.dat持久化带版本号 校验和关闭游戏再打开也不会丢失。️ 6. 中文全链路修复一览问题根因修复??乱码字形码点未包含硬编码字符串补扫 extraStringsFix #217小字看不清CJK 缩放仅 1.1x分段缩放 1.20-1.45xFix #216字体边缘锯齿加载尺寸 144px提升至192pxFix #218全屏中文模糊画布 1280×720 被放大canvasMult 高分辨率 RTFix #221文字与面板重叠缩放后布局参数没同步面板增高、间距加大Fix #219-220️问题 → 根因 → 修复全链路问题中文显示 ??根因字形码点未覆盖问题小字看不清根因CJK 缩放仅 1.1x问题字体边缘锯齿根因加载尺寸 144px问题全屏中文模糊根因画布 1280x720 被放大问题文字溢出面板根因缩放后布局没同步Fix #217补扫硬编码字符串Fix #216分段缩放 1.20-1.45xFix #218加载尺寸 144→192pxFix #221canvasMult 高分辨率 RTFix #219-220面板增高、间距加大 下篇预告第 5 篇持久化、撤销、提示那些非核心功能二进制存档格式与校验和、有限状态撤销栈、Hint 安全格算法、Smiley Face 4 状态设计。真正区分能跑和好用的 80%。 系列目录#标题状态1项目概览与扫雷核心算法✅ 已发布2Raylib 渲染架构✅ 已发布3222 个 Bug 修复教会我的事✅ 已发布4中英文双语的工程实现← 本文✅ 已发布5持久化、撤销、提示非核心功能 待发布6200 个单元测试C 项目也能 TDD 待发布7960×640 → 1280×720全局缩放重构实录 待发布如果你觉得有帮助点赞 收藏 关注三连支持作者继续写下去

相关新闻