LANG 环境变量被强制改为 en_US.UTF-8 问题排查与修复
Fedora 44 上 LANG 被 /etc/profile.d/lang.sh 的 CJK fallback 逻辑强制改为 en_US.UTF-8,并通过 niri-session 的 import-environment 泄漏进 systemd user 环境。本文记录根因分析与 systemd oneshot 覆盖修复方案。
LANG 环境变量被强制改为 en_US.UTF-8 问题排查与修复
主机:Fedora 44 (qbh-x220) · 用户:qibohan · 修复日期:2026-09-09
一、问题现象
桌面环境(niri + systemd user 环境)中 LANG 显示为 en_US.UTF-8,而不是预期的 zh_CN.UTF-8。
导致中文环境变量丢失,影响:
- 应用程序界面语言(切换为英文)
- 中文 locale 相关的排序、日期、分词行为
二、根因分析
1. 罪魁祸首:/etc/profile.d/lang.sh 的 CJK fallback 逻辑
Fedora 的 /etc/profile.d/lang.sh 中包含专门针对虚拟终端(VT / console)的 LANG 修正逻辑:
1 | # The ${LANG} manipulation is necessary only in virtual terminal (a.k.a. console - /dev/tty*): |
触发条件(三个同时满足才生效):
LANG变量非空(且是.utf-8格式)TERM = linux(虚拟终端专用 TERM)- 当前 tty 是
/dev/tty*(true VT / console,不是 ssh 的 pty)
这是 Fedora 的设计行为:CJK 字符集在 Linux 控制台(无图形环境)下无法正常渲染,所以强制回退到 en_US.UTF-8 以保证 console 可用。
2. 泄漏链路:为什么桌面环境也被污染
正常情况下,登录进图形会话(Wayland/niri)后,桌面进程的 LANG 应由 display manager / session 启动器 注入,而不是走 lang.sh。
但当时的链路是:
1 | niri-session 启动 → systemctl --user import-environment → |
如果 niri 启动时(或其上游)恰好经过了一个 TERM=linux + /dev/tty* 的登录路径,lang.sh 就把 LANG 改成了 en_US.UTF-8,并随之导入 systemd user 环境 → 所有由 systemd user 启动的桌面应用都继承到错误的 LANG。
三、排查过程摘要
| 步骤 | 检查项 | 结论 |
|---|---|---|
| 1 | echo $LANG(交互 shell) |
表现为 en_US.UTF-8 |
| 2 | /etc/profile.d/lang.sh 源码审查 |
找到 zh* → en_US.UTF-8 fallback |
| 3 | 确认触发条件 | TERM=linux + /dev/tty* 同时成立 |
| 4 | ~/.config/environment.d/ 检查 |
存在但优先级低于 import-environment |
| 5 | systemd user 环境变量(systemctl --user show-environment) |
LANG 已被污染为 en_US.UTF-8 |
关键发现: niri-session 的 import-environment 注入优先级 高于 environment.d,所以光靠 environment.d/xx-lang.conf 无法覆盖,必须在 systemd user 层面修正。
四、解决方案
创建 systemd user 服务,在 niri 启动前 显式把 LANG 覆盖回 zh_CN.UTF-8:
文件:~/.config/systemd/user/fix-lang.service
1 | [Unit] |
启用命令
1 | systemctl --user daemon-reload |
五、验证结果
1 | # 1. 服务状态 |
均验证通过,桌面应用恢复中文环境。
六、要点总结
- **不要改
/etc/profile.d/lang.sh**:那是 Fedora 对 Linux console 的正确保护(CJK 在 VT 下无法渲染),改它会影响 console 可用性,且会被系统更新覆盖。 - **不要只靠
~/.config/environment.d/**:在该机器上,niri-session的import-environment优先级更高,会覆盖 environment.d 的注入。 - 正确做法:在 systemd user 环境层创建一个 oneshot service,在图形会话启动链中(早于 niri)用
systemctl --user set-environment强制覆盖——这样所有由 systemd user 环境派生的进程(桌面应用、托盘、后台服务)都拿到正确的 LANG。 - 该方案对 systemd user 环境改对环境变量后无需重启,下次
niri-session启动即自动生效;当前会话也已即时生效。