使用gettext实现C++国际化:如何让字符串在定义阶段完成翻译?
gettext国际化:让字符串在定义阶段完成翻译的解决方案
问题背景
使用gettext进行C++国际化开发时,定义的静态字符串A::BASE_PCB和非静态字符串BASE_PCB_2,在定义阶段用__()包裹后并未完成翻译,只有在运行时再次用__()包裹才能得到正确结果。需要实现字符串在定义阶段就完成翻译。
代码与配置示例
main.cpp
#include <iostream> #include <string> #include <libintl.h> #define __(String) gettext(String) class A { public: static const std::string BASE_PCB; }; const std::string A::BASE_PCB = __("BasePCB"); int main() { std::string BASE_PCB_2 = __("BasePCB2"); const std::string path = "/usr/local/share/locale"; std::cout << "Locale path: " << path << std::endl; setlocale(LC_ALL, "fr_CH.utf8"); bindtextdomain("example", path.c_str()); textdomain("example"); std::cout << "Locale: " << setlocale(LC_ALL, nullptr) << std::endl; std::cout << "1. Translation: " << A::BASE_PCB.c_str() << std::endl; std::cout << "2. Translation with __: " << __(A::BASE_PCB.c_str()) << std::endl; std::cout << "3. Translation: " << BASE_PCB_2 << std::endl; std::cout << "4. Translation with __: " << __(BASE_PCB_2.c_str()) << std::endl; return 0; }
fr.po
#: main.cpp:12 msgid "BasePCB" msgstr "Base PCB" #: main.cpp:15 msgid "BasePCB2" msgstr "Base PCB 2"
.mo文件生成与部署命令
msgfmt fr.po -o example.mo cp example.mo /usr/local/share/locale/fr/LC_MESSAGES/example.mo
CMakeLists.txt
cmake_minimum_required(VERSION 3.8) project ( "CppInternationalization" VERSION 1.0.0 LANGUAGES CXX ) # 使用C++20 set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_executable(${PROJECT_NAME} "main.cpp") # 添加libintl find_package(Intl REQUIRED) target_link_libraries(${PROJECT_NAME} PUBLIC ${Intl_LIBRARY}) target_include_directories(${PROJECT_NAME} PUBLIC ${Intl_INCLUDE_DIRS})
执行命令
mkdir build cmake .. && cmake --build . && ./CppInternationalization
当前输出结果
Locale path: /usr/local/share/locale Locale: fr_CH.utf8 1. Translation: BasePCB # <- 预期结果为'Base PCB' 2. Translation with __: Base PCB 3. Translation: BasePCB2 # <- 预期结果为'Base PCB 2' 4. Translation with __: Base PCB 2
问题原因
C++中,静态成员变量A::BASE_PCB的初始化发生在main()函数执行之前,而setlocale、bindtextdomain、textdomain这些gettext核心配置函数都在main()内调用。此时gettext未完成本地化环境初始化,无法加载翻译文件,定义阶段调用__()只能得到原字符串。
非静态的BASE_PCB_2虽然在main()内定义,但初始化时机早于gettext配置函数执行,同样无法完成翻译。
解决方案
要让字符串在定义/初始化阶段完成翻译,必须确保gettext的初始化逻辑在字符串初始化之前执行,以下两种方法可实现:
方法1:全局静态初始化类(推荐)
将gettext初始化逻辑封装到全局静态类中,利用C++静态对象初始化顺序,让配置逻辑先于目标字符串执行:
修改后的main.cpp:
#include <iostream> #include <string> #include <libintl.h> #define __(String) gettext(String) // 全局初始化类,确保gettext配置先执行 class GettextInitializer { public: GettextInitializer() { const std::string path = "/usr/local/share/locale"; setlocale(LC_ALL, "fr_CH.utf8"); bindtextdomain("example", path.c_str()); textdomain("example"); } }; // 全局静态对象,在main之前初始化 static GettextInitializer gettext_init; class A { public: static const std::string BASE_PCB; }; const std::string A::BASE_PCB = __("BasePCB"); int main() { // 此时gettext已初始化完成 std::string BASE_PCB_2 = __("BasePCB2"); const std::string path = "/usr/local/share/locale"; std::cout << "Locale path: " << path << std::endl; std::cout << "Locale: " << setlocale(LC_ALL, nullptr) << std::endl; std::cout << "1. Translation: " << A::BASE_PCB.c_str() << std::endl; std::cout << "2. Translation with __: " << __(A::BASE_PCB.c_str()) << std::endl; std::cout << "3. Translation: " << BASE_PCB_2 << std::endl; std::cout << "4. Translation with __: " << __(BASE_PCB_2.c_str()) << std::endl; return 0; }
方法2:延迟字符串初始化
将静态成员变量改为函数内的静态变量,或在gettext初始化完成后再初始化字符串:
修改后的main.cpp示例:
#include <iostream> #include <string> #include <libintl.h> #define __(String) gettext(String) class A { public: // 改为静态函数,第一次调用时初始化 static const std::string& BASE_PCB() { static const std::string val = __("BasePCB"); return val; } }; int main() { const std::string path = "/usr/local/share/locale"; std::cout << "Locale path: " << path << std::endl; setlocale(LC_ALL, "fr_CH.utf8"); bindtextdomain("example", path.c_str()); textdomain("example"); // 在gettext初始化后再初始化字符串 std::string BASE_PCB_2 = __("BasePCB2"); std::cout << "Locale: " << setlocale(LC_ALL, nullptr) << std::endl; std::cout << "1. Translation: " << A::BASE_PCB().c_str() << std::endl; std::cout << "2. Translation with __: " << __(A::BASE_PCB().c_str()) << std::endl; std::cout << "3. Translation: " << BASE_PCB_2 << std::endl; std::cout << "4. Translation with __: " << __(BASE_PCB_2.c_str()) << std::endl; return 0; }
验证结果
修改后执行程序,输出会变为:
Locale path: /usr/local/share/locale Locale: fr_CH.utf8 1. Translation: Base PCB 2. Translation with __: Base PCB 3. Translation: Base PCB 2 4. Translation with __: Base PCB 2
内容的提问来源于stack exchange,提问作者Molitoris
相关产品推荐
相关产品推荐

