在 Linux 桌面环境中,Rime 输入法具有很强的可定制能力。除了词库、快捷键和候选数量,也可以借助 Lua 动态生成当前日期、时间和星期。

目标效果如下:

riqi      → 2026年7月26日、2026-07-26、2026/07/26
shijian   → 10:35、10:35:42
xianzai   → 2026-07-26 10:35:42
xingqi    → 星期日

这类功能看似只是增加几行配置,实际排查过程中却遇到了几个容易误判的问题:

  • 编译配置中已经存在 Lua translator,但候选没有出现;
  • 部署命令返回成功,不等于功能实际可用;
  • 候选窗口出现,不等于动态候选出现;
  • 当前图形会话使用的输入法,可能并不是正在配置的 Rime;
  • 直接修改 build/ 目录虽然暂时有效,却会在重新部署后被覆盖;
  • 在交互式终端中使用严格退出选项,可能导致整个终端窗口关闭。

本文整理完整过程,并总结一套更可靠的处理方法。


一、运行环境与需求

测试环境为:

  • Arch Linux
  • KDE Plasma Wayland
  • Fcitx5
  • Fcitx5-Rime
  • librime 1.17
  • librime-lua 插件
  • 朙月拼音简化字方案:luna_pinyin_simp

用户原本已经将每页候选数量改为 9,但修改位置位于:

<Rime 用户目录>/build/

build/ 是 Rime 的编译产物目录。这里的文件会在重新部署时被重新生成,因此不适合保存长期自定义配置。

正确做法是把自定义内容放在用户数据目录:

<Rime 用户目录>/

例如:

<Rime 用户目录>/
├── rime.lua
├── lua/
│   └── date_time.lua
├── luna_pinyin_simp.custom.yaml
└── build/

二、先确认 Lua 支持是否存在

动态日期和时间需要 librime-lua

在 Arch Linux 中,新版 librime 软件包通常已经包含 Lua 插件,不一定需要额外安装独立软件包。

可以检查:

pacman -Q fcitx5 fcitx5-rime librime rime-luna-pinyin lua

再确认插件文件:

ls -l <librime-lua 插件路径>
pacman -Qo <librime-lua 插件路径>

如果能看到对应的 Lua 插件文件,并且该文件属于 librime 软件包,说明 Lua 支持已经安装。


三、不要继续修改 build 目录

候选数量应写入:

luna_pinyin_simp.custom.yaml

例如:

patch:
  "menu/page_size": 9

动态 Lua translator 也应通过同一个补丁挂载:

patch:
  "menu/page_size": 9
  "engine/translators/@before 3": lua_translator@date_time_translator

这样重新部署后:

  • 每页仍然显示 9 个候选;
  • Lua translator 会继续存在;
  • 不需要再次手工修改编译后的 schema。

四、第一次配置:编译成功,但功能没有出现

最初的配置采用了单文件内联 Lua 写法,将 translator 直接定义在 rime.lua 中。

部署命令返回:

rime_deployer 返回值:0
fcitx5-remote 返回值:0

编译后的 schema 也能找到:

lua_translator@date_time_translator

候选数量同样保持为:

menu:
  page_size: 9

从静态配置看,一切似乎都成功了。

但实际输入时:

riqi

候选仍然只是:

日期
日起
日企
……

输入:

shijian

看到的仍然是:

时间
事件
实践
……

输入 xianzaixingqi 也只是普通拼音词典候选,没有任何动态日期、时间或星期。

这说明:

配置被编译进去,不代表 Lua translator 在运行时真正生成了候选。


五、不能把“候选窗口出现”当作成功

排查过程中曾经出现一个典型误判:

  • 自动化测试输入了 riqi
  • 屏幕上出现了 Rime 候选窗口;
  • 因此被报告为“真实验证成功”。

但截图中显示的仍然只是普通词典候选,并没有当前日期。

真正的成功标准必须是候选中明确出现动态值,例如:

2026年7月26日
2026-07-26
2026/07/26
20260726
2026年7月26日 星期日

同理:

shijian

必须出现测试时的真实时间,而不是只有“时间”这个普通词语。

因此验收时不能只检查:

  • rime_deployer 是否返回 0;
  • schema 中是否包含 Lua translator;
  • 候选窗口是否出现。

必须检查:

  • 动态候选内容是否真实存在;
  • 日期和时间是否与系统当前时间一致;
  • 候选是否位于可见页;
  • 当前输入法是否确实是 Rime。

六、一个容易忽略的根因:当前输入法不是 Rime

继续排查后发现,图形会话中实际激活的输入法一度不是 Rime,而是:

keyboard-us

这种情况下,即使 Rime 配置完全正确,也不会由 Rime 处理输入。

检查当前输入法:

fcitx5-remote -n

如果返回:

keyboard-us

说明当前并未使用 Rime。

切换到 Rime:

fcitx5-remote -s rime
fcitx5-remote -o

再次检查:

fcitx5-remote -n

预期返回:

rime

这一步非常关键。

在输入法排查中,经常会花大量时间检查词库、Lua、schema 和日志,却忽略了当前正在运行的根本不是目标输入法。


七、采用标准 Lua 模块结构

为了避免单文件内联方式在不同版本之间产生加载差异,最终改为更标准的模块化结构。

1. rime.lua

路径:

<Rime 用户目录>/rime.lua

内容:

date_time_translator = require("date_time")

rime.lua 只负责加载入口,不再保存全部 translator 实现。

2. lua/date_time.lua

路径:

<Rime 用户目录>/lua/date_time.lua

该模块负责:

  • 判断输入编码;
  • 读取系统时间;
  • 生成日期、时间、星期候选;
  • 返回 translator 函数。

结构示意如下:

local weekdays = {
    [1] = "星期日",
    [2] = "星期一",
    [3] = "星期二",
    [4] = "星期三",
    [5] = "星期四",
    [6] = "星期五",
    [7] = "星期六",
}

local function translator(input, seg)
    local now = os.date("*t")
    local weekday = weekdays[now.wday]

    local function emit(text, comment)
        local candidate = Candidate(
            "date_time",
            seg.start,
            seg._end,
            text,
            comment
        )

        candidate.quality = 1000
        yield(candidate)
    end

    if input == "riqi" then
        emit(
            string.format(
                "%d年%d月%d日",
                now.year,
                now.month,
                now.day
            ),
            "日期"
        )

        emit(os.date("%Y-%m-%d"), "日期")
        emit(os.date("%Y/%m/%d"), "日期")
        emit(os.date("%Y%m%d"), "日期")

        emit(
            string.format(
                "%d年%d月%d日 %s",
                now.year,
                now.month,
                now.day,
                weekday
            ),
            "日期与星期"
        )

    elseif input == "shijian" then
        emit(os.date("%H:%M"), "时间")
        emit(os.date("%H:%M:%S"), "时间")

    elseif input == "xianzai" then
        emit(os.date("%Y-%m-%d %H:%M:%S"), "当前日期时间")

    elseif input == "xingqi" then
        emit(weekday, "星期")
    end
end

return translator

这里给候选设置较高的 quality,是为了避免动态候选被普通拼音词典候选压到后面。

如果 Lua translator 已经运行,但动态候选排在第二页甚至更后面,看起来也会像“功能没有生效”。


八、重新部署与重新加载

标准部署命令:

RIME_DIR="<Rime 用户目录>"

rime_deployer \
    --build \
    "$RIME_DIR" \
    "<Rime 共享数据目录>" \
    "$RIME_DIR/build"

重新加载 Fcitx5:

fcitx5-remote -r

必要时重启 Fcitx5 用户服务:

systemctl --user restart <Fcitx5 用户服务名称>

然后重新切换到 Rime:

fcitx5-remote -s rime
fcitx5-remote -o

服务名称可能因会话而异,不应在其他机器上直接照抄。可以先查询:

systemctl --user list-units | grep -i fcitx

九、避免在交互式终端中使用 set -e

排查期间还出现了一个与输入法无关、但很容易引起误会的问题。

一段命令开头使用了:

set -euo pipefail

随后某条检查命令返回非零状态,导致当前交互式 Bash 直接退出。

如果终端模拟器设置为“Shell 退出后关闭窗口”,表现就会像:

一执行命令,整个终端突然关闭。

这并不代表终端损坏。

其中:

  • set -e:命令失败时退出 Shell;
  • set -u:引用未定义变量时退出;
  • set -o pipefail:管道中任一命令失败,整个管道视为失败。

这些选项适合放在独立脚本或子 Shell 中,不适合直接粘贴进当前正在使用的交互式终端。

错误示例:

set -euo pipefail
command -v some_command

更安全的交互式写法是:

command -v some_command || echo "未找到命令"

或者:

(
    set -euo pipefail
    # 需要严格检查的命令
)

即使子 Shell 退出,也不会关闭当前终端。


十、修改前应保留可追溯备份

Rime 配置属于长期使用的个人资产。修改前应保留原文件,并使用可追溯命名。

推荐格式:

原文件名.YYYYMMDD-HHMMSS

时间戳取原文件的修改时间,而不是备份执行时间。

例如:

FILE="<Rime 用户目录>/rime.lua"

if [ -e "$FILE" ]; then
    STAMP="$(date -r "$FILE" '+%Y%m%d-%H%M%S')"
    cp -a -- "$FILE" "${FILE}.${STAMP}"
fi

不建议使用:

rime.lua.bak
rime.lua.bak.1
rime.lua.old

这些命名难以判断备份对应的具体版本和时间。


十一、最终验收结果

完成修复后,真实图形会话中的测试结果为:

输入 riqi

候选出现:

2026年7月26日
2026-07-26
2026/07/26
20260726
2026年7月26日 星期日

输入 shijian

候选出现当前系统时间:

10:35
10:35:42

输入 xianzai

候选出现完整日期时间:

2026-07-26 10:35:42

输入 xingqi

候选出现:

星期日

同时确认:

fcitx5-remote -n

返回:

rime

每页候选数量仍然保持为:

menu:
  page_size: 9

用户词库、学习数据和同步数据均未删除。


十二、这次排查最重要的经验

1. 部署成功不等于功能成功

exit 0

只能说明部署程序没有报告错误,不能证明动态功能已经出现在输入法中。

2. 编译配置存在不等于运行时生效

即使 schema 中能够找到:

lua_translator@date_time_translator

也仍需验证 Lua 是否实际加载并产生候选。

3. 候选窗口出现不等于目标候选出现

普通词库同样会显示候选窗口。验收必须检查具体候选内容。

4. 首先确认当前输入法

在深入研究 Lua 之前,应先运行:

fcitx5-remote -n

确认当前确实是 rime

5. 动态候选可能被普通词典压到后面

Lua translator 即使正常运行,也可能因为候选权重不足而排到后页。必要时应提高候选 quality

6. 用户配置不要写进 build

长期配置应放在:

*.custom.yaml
rime.lua
lua/

build/ 只用于编译结果和验收。

7. 自动化验收必须检查结果语义

截图中出现候选框,只能证明输入法做出了响应。只有截图中真正出现当前日期、时间和星期,才算功能完成。


结语

Rime 的动态日期时间功能本身并不复杂,真正容易出错的是配置层、编译层、运行层和当前输入法状态之间的混淆。

一套可靠的处理顺序应当是:

确认 Lua 插件
→ 确认当前输入法为 Rime
→ 使用用户层 custom 配置
→ 使用标准 Lua 模块结构
→ 重新部署
→ 重新加载
→ 在真实应用中检查动态候选

只要把“静态配置正确”和“运行结果正确”分开验证,这类问题就能得到稳定、可重复的解决。

Leave a Reply

Your email address will not be published. Required fields are marked *