本文针对 Linux 环境下终端 Emoji 符号渲染异常问题(如区域指示符无法正确显示为区域的旗帜), 从字体配置与终端工具两个角度展开分析. 通过排查 Alacritty 的字体回退机制、尝试合并多字体方案, 最终定位到 Unicode 版本兼容性问题, 通过切换至 Wezterm 终端并配置 Unicode 版本正确渲染国旗 Emoji. 文中记录了在尝试过程中的字体选择(Iosevka、思源黑体、Sarasa 更纱黑)、终端对比(Alacritty 与 Wezterm)及配置实现过程, 为无桌面环境下的 Linux 字体管理提供实践参考.
前言
在使用 Alacritty 的过程中, 忽然发现一些 emoji 的显示不够好, 例如区域指示符无法正确渲染成旗子, 而是还是 JP、US 这样的符号. 此前一直没有察觉到, 以为就是这样子的… 但其实是因为某些软件自身对字体的支持不足或是因为错误的配置, 导致单独的两个字符无法渲染成一个旗帜图标..
例如, 对于同一段文本来说, 失败的渲染会将中国国旗显示成 CN:


为此, 尝试找到解决方案, 并且与我的 Linux 系统上其余涉及到字体的应用保持兼容一致.
在解决问题之前, 我的环境为:
-
Bspwm 环境, 无集成 DE, 使用 fontconfig 管理全局字体
-
Alacritty 终端, Neovim 文本编辑器
-
字体: Iosevka 系列 + Source Han Sans CN 思源黑无衬线的中文支持
字体更换
查询此类问题产生的原因, 发现最大的可能性还是在于字体的缺失或未能成功让 emoji 缺省到该字体上, 那么第一步考虑的便是安装 noto color emoji
, 并配置应用使用该字体作为 emoji 的缺省.
最开始在查阅 Alacritty 文档时心都凉了半截: RFC: Font Configuration · Issue #957 · alacritty/alacritty 字体的缺省方案居然从 2017 年就开始开始讨论, 真是好事, 大家都很需要它, 但怎么到了 2025 Issue 还是个 Open 的 Issue.. 🤡
目前至少看起来 Alacritty 是不支持应用内的字体缺省的, 只能支持一个默认的字体:
1[font.normal]
2family = "Iosevka Nerd Font"
对于其余的字体呢? 通过 alacritty -vvv
可以看到更加详细的日志; 在新打开的终端中输入中文与 Emoji, 在日志中可以看到:
1[1.484105069s] [TRACE] [alacritty_terminal] Setting attribute: Foreground(Named(Foreground))
2[1.484673432s] [TRACE] [crossfont] Got font path="/usr/share/fonts/noto/NotoColorEmoji.ttf", index=0
3[1.484719904s] [DEBUG] [crossfont] Loaded Face Face { ft_face: Font Face: Regular, load_flags: LoadFlag(MONOCHROME | TARGET_MONO | COLOR), render_mode: "Mono", lcd_filter: 1 }
4[1.485993927s] [TRACE] [crossfont] Got font path="/usr/share/fonts/adobe-source-han-sans/SourceHanSansCN-Bold.otf", index=0
5[1.487011210s] [DEBUG] [crossfont] Loaded Face Face { ft_face: Font Face: Bold, load_flags: LoadFlag(TARGET_LIGHT), render_mode: "Lcd", lcd_filter: 1 }
Alacritty 是能够自动去获取一些缺省字体的, 因此其本质上还是依赖于 fontconfig
中定义的各种规则. 而且可以看到, 对于 Emoji 来说, 其也能正确地去获取相应的字体渲染, 但还是无法正确地将地区指示符渲染成符号…

更纱黑(Sarasa)字体
由于不确定是不是字体的问题, 我又尝试了更换新的字体, 并尝试向其中手动打入补丁, 设想的流程大概是:
1获取英文字体 ----> 合并字体 -------> 合并字体 ----------> 超级字体文件
2 | | |
3获取中文字体 --| 获取 NerdFont --| 获取 NotoColorEmoji |
由于我使用的 Iosevka + 思源黑就是 更纱黑的中英文字体选择, 因此尝试以该文件作为基版, 来不断地 patch 上各个 emoji.
有关更纱黑体的各个版本, 可以看: 更纱黑体这么多版本,要怎么选? - 知乎


我平常一直都是使用的无称线的 Iosevka, 但是 Slab 版本的有称线看起来也不错, 感觉文字更加有质感:
但大段代码都是有棱角的时候感觉也是怪怪的🤔


但是 Fixed 版本的字体有个问题: 等宽字体自带的 Emoji 过小! 虽然 Patch 上去的 Emoji 看起来是正常大小的(比如 Folder, Image 的图标), 但字体内部本身也会包含一些简单的 Emoji(比如★), 我也没有高兴仔细研究如何去做强制覆盖, 就有一些搞不定..

至于为什么不用非等宽字体.. 那是因为 Sarasa 的非等宽字体只有 Gothic 和 UI 这两种 Style, 这两种使用的英文字体是 inter, 我都不知道怎么会有这么丑的字体, 还是因为我这边配置不对把它渲染丑了:


非等宽的更纱黑实在是让这么一个大和抚子般名字的字体蒙羞了..
但是我又不喜欢 Sarasa 的连笔, 会将 > 和 = 渲染成大于等于 >= 🤔 作为文章什么的展示出来可能还行, 但是写代码的时候我是不喜欢看到这种东西的..
总之, 在尝试更纱黑时, 还没有涉及到如何合并 NotoColorEmoji, 就已经让我放弃它了.
终端更换
当我意识到可能更换字体并非明智之举时, 我开始将解决思路转向了更换一个对字体支持更好的 Terminal 上. 毕竟对于彩色的 Emoji 来说, 将其 Patch 到一个黑白的字体上可能不是很好, 大概率会有奇怪的问题, 不该去尝试合并出来一个超级字体去做适配.
在 Alacritty 的 Issue 下, 我发现了一个新的 Rust 开发、GPU 加速的终端: wezterm/wezterm: A GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust
其对于字体的支持可以说是相当方便:
1-- 字体配置
2config.font = wezterm.font_with_fallback({
3 'Iosevka Nerd Font',
4 'Source Han Sans CN',
5 'Noto Color Emoji',
6})
7config.font_size = 13
That’s all. 很轻松地就能够在 Wezterm 下配置我希望的缺省字体. 但配置完成后国旗还是无法正确显示, Sonnet 告诉我可能是 Unicode 版本的原因, 在 Wezterm 配置中加入:
1config.unicode_version = 14
居然非常轻松就能让 Wezterm 正确地显示国旗了!
最开始完全没有考虑过 Unicode 也有版本(可能是有, 但仅仅认为是一些无足轻重的符号的加入/移除等), 但事实上 Unicode 版本也会影响终端展示这些符号的规则.
至此我认为 Wezterm 在功能上与易用性上都已经是战胜了 Alacritty 了, 但仅仅是这样还不足以完全诱惑我迁移过来, 毕竟迁移也是有一些时间成本和难度曲线的. 真正让我下定决心的还是其一眼看上去就更加丰富的功能, 以及使用 Lua 进行配置文件管理的配置潜能, 这可比 Toml 的配置文件看起来有能力多了!
正如开发者 Wez 所说( discussions #1769):
I’m a programmer and I want a programmers terminal
这太酷了! 尽管 Alacritty 确实会更快一点, 但是功能也确实是更少一些, 使用 toml 进行配置的行为也看上去不是很酷..
- 在使用
joshuto
/nvim
进行滚动时, 确实明显能够感受到 wezterm 会卡顿一些, 特别是我的这台电脑的 CPU/GPU 都不是很好(不超过50块), 感受会更加明显.. 因此甚至最开始还有一些劝退..
至此将终端切换为 Wezterm, 总算是抛弃了老旧的 Alacritty(在之前尝试使用 joshuto 预览图片时就已经痛苦万分), 拥抱新的工具!