一、问题
今天新安装了 Debian 13 虚拟机,发现每次启动时都会在 GRUB 菜单等待 5 秒
由于几乎不会使用到 GRUB 菜单,因此打算修改等待时间
编辑 GRUB 配置文件 /etc/default/grub 修改 GRUB_TIMEOUT=5 为 GRUB_TIMEOUT=1
然后重新生成 GRUB 配置:
update-grub重启后发现:GRUB 菜单显示语言从中文变成了英文
虽然这不会影响系统启动,但是我很好奇是什么导致显示发生了变化
二、理论判断
GRUB_TIMEOUT 只控制 GRUB 菜单等待时间,因此它不会影响 GRUB 菜单语言
因此问题应定位在执行 update-grub 重新生成 /boot/grub/grub.cfg 的过程中
查阅到 GRUB 菜单的多语言显示依赖 gettext 国际化机制
如果需要修改 GRUB 菜单语言,需要在 GRUB 配置中启用 gettext 模块
并配置对应的语言环境参数,使 GRUB 在启动阶段能够加载相应的语言翻译文件
三、确认 GRUB 是否加载 gettext 国际化模块
如果生成的 GRUB 配置文件中未加载 gettext 模块,或者未配置对应的 locale 信息
那么 GRUB 在启动时不会加载语言翻译文件,最终只能显示为英文
首先检查已生成的 GRUB 配置文件:
grep gettext /boot/grub/grub.cfg执行后没有任何输出,说明当前生成的 /boot/grub/grub.cfg 中未包含 gettext 模块相关配置
因此可以确认当前 GRUB 配置未启用 gettext 国际化支持,启动时不会加载对应的 locale 翻译文件
四、排查为什么没有生成 gettext
update-grub 本质上是对 grub-mkconfig 的封装
grub-mkconfig 根据 /etc/grub.d/ 下的生成脚本以及相关配置动态生成 /boot/grub/grub.cfg
首先查看 GRUB 配置生成逻辑:
cat /etc/grub.d/00_header关键代码如下:
grub_lang=`echo $LANG | cut -d . -f 1`
# Gettext variables and moduleif [ "x${grub_lang}" != "xC" ] && [ "x${LANG}" != "xPOSIX" ] && [ "x${LANG}" != "x" ]; then cat << EOF set locale_dir=\$prefix/locale set lang=${grub_lang} insmod gettextEOFfi从上述逻辑可以看出,gettext 配置的生成依赖当前环境变量 LANG
如果执行 update-grub 时环境变量 LANG=C,grub-mkconfig 会跳过 gettext 国际化配置,最终生成的 /boot/grub/grub.cfg 中不会包含 gettext 模块加载信息
可以在脚本中看到只有 grub_lang 不为 C,或 LANG 不为 POSIX 或空时才会生成 gettext 配置
因此需要具体检查当前的环境变量的 LANG 值:
echo $LANG发现执行结果为 C ,所以 update-grub 执行过程中跳过了 gettext 配置生成
五、进一步排查:为什么 LANG 会是 C?
Linux 中系统默认 locale 配置通常由 /etc/default/locale 定义
正常情况下用户登录后,系统初始化流程会读取该配置,并将其中的语言环境变量导入当前 shell 环境
cat /etc/default/localeLANG="zh_CN.UTF-8"LANGUAGE="zh_CN:zh"既然 /etc/default/locale 定义了 LANG,但是 echo $LANG 仍然输出为 C
说明系统默认 locale 配置本身没有问题,而是在登录环境初始化过程中,LANG 的值被其他配置或脚本覆盖
六、确认 SSH 登录阶段是否加载 /etc/default/locale
首先确认 SSH 登录过程中是否通过 PAM 加载系统 locale 配置:
grep pam_env /etc/pam.d/sshdsession required pam_env.sosession required pam_env.so envfile=/etc/default/locale说明 SSH 登录流程中已经启用了 pam_env 模块,并且会读取 /etc/default/locale 中定义的环境变量
因此,按照正常流程:
/etc/default/locale ↓pam_env.so 加载 ↓SSH 登录会话 ↓shell 环境变量当前 shell 应该继承:
LANG=zh_CN.UTF-8但是 echo $LANG 输出为 C 说明 /etc/default/locale 的加载过程正常
LANG 并非在 SSH 登录阶段丢失,而是在后续 shell 初始化过程中被其他配置重新赋值覆盖
七、检查 shell 启动脚本中的环境变量设置
由于已经确认 SSH 登录阶段能够通过 pam_env 模块加载 /etc/default/locale,因此可以排除系统 locale 配置未生效的问题
所以需要继续检查 bash 启动脚本中是否存在对 LANG 或 LANGUAGE 的设置
bash 登录过程涉及的主要配置文件包括:
/etc/profile/etc/profile.d/*~/.profile~/.bashrc执行以下命令进行排查:
grep -R "LANG" /etc/profile /etc/profile.d /root/.profile /root/.bashrc检查结果发现存在以下配置:
export LANG=Cexport LANGUAGE=C进一步确认配置来源于 /root/.profile,文件内容如下:
# Installed by Debian Installer:# no localization for root because zh_CN.UTF-8# cannot be properly displayed at the Linux consoleexport LANG=Cexport LANGUAGE=C我在安装系统的时候选择了中文,Debian Installer 在安装系统时检测到 locale 使用的是 zh_CN.UTF-8
但由于 root 用户主要用于 Linux 控制台环境,而传统 Linux console 无法正常显示中文
因此在 /root/.profile 中为 root 用户设置了:
LANG=CLANGUAGE=C所以当 root 用户启动登录 shell(例如 SSH 登录 root、执行 su - 或 sudo -i)时,bash 会加载 /root/.profile,其覆盖了 pam_env 从 /etc/default/locale 加载的:
LANG=zh_CN.UTF-8LANGUAGE=zh_CN:zh最终导致 update-grub 生成配置文件时跳过了 gettext 配置
八、修复
由于执行 update-grub 时系统语言环境未正确设置,导致 grub-mkconfig 根据当前 locale 生成了英文菜单配置
所以修改 /root/.profile 配置文件,删除原有语言配置,并添加:
export LANG=zh_CN.UTF-8export LANGUAGE=zh_CN:zh使配置立即生效:
source /root/.profile确认 echo $LANG 输出 zh_CN.UTF-8
重新生成配置文件,再次检查发现已经包含 gettext 和 lang 等语言配置
重启系统进行验证,确认 GRUB 菜单中文显示已恢复正常
九、总结
安装系统时选择了中文语言环境,Debian Installer 会根据安装选项生成对应的 locale 配置,因此系统安装完成后 GRUB 菜单可以正常显示中文
进入系统后,由于传统 Linux console(TTY)不支持显示中文字符,因此系统对 console 环境进行了调整
所以重新生成的 GRUB 配置文件缺少对应的语言设置,所以导致 GRUB 菜单无法继续显示中文
非商业用途可以使用,但必须注明出处;
若有改编需采用相同许可协议发布。