🌍 使用 CMake 一键构建多语言程序:从 gettext 到自动生成 .mo 文件的完整实践

摘要:
本文详细介绍如何使用 GNU gettextCMake 实现 C 项目的多语言国际化(i18n)自动化构建流程。通过一个简洁可运行的示例,你将学会如何自动提取源码中的可翻译字符串、生成 .po 翻译文件、编译 .mo 语言包,并在程序中动态切换语言。本文提供完整的 CMake 脚本示例 C 代码,一键构建即可体验中、英、日、俄多语言输出。


💡 一、什么是 gettext?

gettext 是 Linux 和类 UNIX 系统中最主流的国际化解决方案。
它的核心思想是:

把程序中的人类可读文本与实际显示的语言解耦,通过 .po 翻译文件动态加载。

当程序运行时,会根据系统语言(LANG=zh_CN.UTF-8 等)自动选择相应语言的翻译文件。


⚙️ 二、项目结构

这是我们最终的示例项目结构,CMake 会自动生成 i18n 文件夹及翻译内容:

gettext_example/
├── src/
│   └── main.c
├── CMakeLists.txt
└── build/
    └── i18n/
        ├── lang.pot
        ├── po/
        │   ├── zh_CN.po
        │   ├── ru.po
        │   └── ja.po
        └── locale/
            └── zh_CN/LC_MESSAGES/lang.mo

🧠 三、C 源码示例:最小可运行多语言程序

src/main.c

#include <stdio.h>
#include <locale.h>
#include <libintl.h>

#define _(s) gettext(s)

int main(void) {
    // 设置系统语言(自动检测环境变量 LANG)
    setlocale(LC_ALL, "");

    // 指定翻译域与目录
    bindtextdomain("lang", "i18n/locale");
    textdomain("lang");

    printf(_("Hello!\n"));
    printf(_("Welcome to the gettext demo.\n"));
    printf(_("This program shows how to switch languages.\n"));

    const char *name = "cheungxiongwei";
    printf(_("Nice to meet you, %s!\n"), name);

    return 0;
}

✅ 输出示例:

英文环境

LANG=en_US.UTF-8 ./main
Hello!
Welcome to the gettext demo.
This program shows how to switch languages.
Nice to meet you, Anya!

中文环境

LANG=zh_CN.UTF-8 ./main
你好!
欢迎使用 gettext 示例。
这个程序展示了如何切换语言。
很高兴认识你,cheungxiongwei!

🏗️ 四、CMake 自动化脚本

以下脚本会自动完成以下任务:

  1. 提取源文件中的 _() 翻译字符串;
  2. 自动初始化 .po 翻译文件;
  3. 自动合并 .pot 更新;
  4. 自动编译 .mo 二进制语言包;
  5. 支持多语言(中文、俄语、日语)并行处理。

CMakeLists.txt

注意:仅限 Linux 运行,CMakeLists.txt 脚本没做跨平台处理。

cmake_minimum_required(VERSION 3.10)
project(gettext_example C)

# 主程序
add_executable(main src/main.c)

# 查找 gettext 工具
find_program(XGETTEXT xgettext REQUIRED)
find_program(MSGINIT msginit REQUIRED)
find_program(MSGMERGE msgmerge REQUIRED)
find_program(MSGFMT msgfmt REQUIRED)

# 语言配置
set(LINGUAS "zh_CN.UTF-8" "ru" "ja" CACHE STRING "List of languages to support")
set(TEXT_DOMAIN "lang")

# 源码文件
file(GLOB_RECURSE SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.c")

if(NOT SRC_FILES)
  message(FATAL_ERROR "No source files found under src/")
endif()

foreach(f ${SRC_FILES})
  list(APPEND SRC_FILES_ABSOLUTE "${CMAKE_CURRENT_SOURCE_DIR}/${f}")
endforeach()

# 输出目录配置
set(GEN_DIR "${CMAKE_BINARY_DIR}/i18n")
set(POT_FILE "${GEN_DIR}/${TEXT_DOMAIN}.pot")
set(PO_DIR "${GEN_DIR}/po")
set(LOCALE_DIR "${GEN_DIR}/locale")

# 提取翻译字符串
add_custom_target(i18n-extract-pot
  COMMENT "Extracting translatable strings -> ${POT_FILE}"
  COMMAND ${CMAKE_COMMAND} -E make_directory ${GEN_DIR}
  COMMAND ${XGETTEXT} --from-code=UTF-8 --output=${POT_FILE} --no-location -k_ ${SRC_FILES_ABSOLUTE}
  COMMAND sed -i "s/charset=CHARSET/charset=UTF-8/" ${POT_FILE}
  DEPENDS ${SRC_FILES_ABSOLUTE}
)

# 生成多语言任务
foreach(lang ${LINGUAS})
  set(PO_FILE "${PO_DIR}/${lang}.po")
  set(MO_PATH "${LOCALE_DIR}/${lang}/LC_MESSAGES")
  set(MO_FILE "${MO_PATH}/${TEXT_DOMAIN}.mo")

  add_custom_target(i18n-translations-${lang}
      COMMAND ${CMAKE_COMMAND} -E make_directory ${PO_DIR}
      COMMAND sh -c "if [ ! -f \"${PO_FILE}\" ]; then echo \"Initializing ${PO_FILE}\"; ${MSGINIT} --input=${POT_FILE} --output-file=${PO_FILE} --locale=${lang} --no-translator; fi"
      COMMAND sh -c "if [ -f \"${PO_FILE}\" ]; then echo \"Merging ${POT_FILE} -> ${PO_FILE}\"; ${MSGMERGE} --update --backup=none ${PO_FILE} ${POT_FILE} || true; fi"
      COMMAND ${CMAKE_COMMAND} -E make_directory ${MO_PATH}
      COMMAND sh -c "echo \"Compiling ${PO_FILE} -> ${MO_FILE}\"; ${MSGFMT} --output-file=${MO_FILE} ${PO_FILE}"
      VERBATIM
  )
  add_dependencies(i18n-translations-${lang} i18n-extract-pot)
endforeach()

🚀 五、构建与测试

mkdir build && cd build
cmake ..
make
make i18n-translations-zh_CN
LANG=zh_CN.UTF-8 ./main

✅ 效果:
项目将自动生成 .po.mo 文件,你只需编辑 .po 即可实现新语言翻译。

#, c-format
msgid "Hello!\n"
msgstr "你好!\n"

#, c-format
msgid "Welcome to the gettext demo.\n"
msgstr "欢迎使用 gettext 示例。\n"

#, c-format
msgid "This program shows how to switch languages.\n"
msgstr "这个程序展示了如何切换语言。\n"

#, c-format
msgid "Nice to meet you, %s!\n"
msgstr "很高兴认识你,cheungxiongwei!\n"


🏁 六、总结

通过本文你学会了:

  • 如何用 CMake 自动化 gettext 国际化流程;
  • 如何生成 .po.mo 文件;
  • 如何通过环境变量切换程序语言;
  • 如何构建一个真正可运行的多语言 C 项目。

✨ 只需执行 make i18n-translations-zh_CN,你的程序即可支持中文输出。
这套方案轻量、可移植,非常适合 嵌入式系统CLI 工具、或 跨平台 C 应用 使用。


Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐