【问题标题】:setlocale() for LC_MESSAGES to non-existing locale fails将 LC_MESSAGES 设置为不存在的语言环境的 setlocale() 失败
【发布时间】:2016-04-26 07:17:15
【问题描述】:

对于嵌入式软件项目,我正在添加对翻译的支持,并且由于我们运行的是嵌入式 Linux,因此我选择使用 libc gettext()。我们没有安装任何语言环境定义,所以我只尝试将LC_MESSAGES 语言环境设置为我想要的语言环境:

setlocale(LC_MESSAGES, "fake");

(我使用名称 fakefake.mo 文件进行伪翻译,然后再进行正确的翻译)。

这在静态链接时工作正常,它返回一个语言环境句柄,bindtextdomain() 和朋友都工作正常,我从中得到我的“翻译”字符串:

setlocale() returned "fake"
current textdomain is "ewe"
current base directory is "/opt/btech/probe/share/locale/WA"
current LC_MESSAGES locale is "fake"
gettext("Error") ==> "Ḗřřǿř"

现在,当我动态编译它时,它不起作用。既不在目标设备上,也不在我的 PC 上本地(文件安装方式相同)。 setlocale() 调用失败,返回 NULL 指针并将 errno 设置为 ENOENT(找不到文件)。在setlocale() 处,我还没有将bindtextdomain() 指向我的文件所在的位置,但是切换调用并没有帮助。

我做错了吗,我上面的工作示例是否错误并且不应该真正起作用?我是否需要为我调用 setlocale() 的任何内容(甚至是 LC_MESSAGES)定义语言环境?

这是测试二进制文件的来源:

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

int main()
{
    const char *l = setlocale(LC_MESSAGES, "fake");

    printf("setlocale() returned \"%s\"\n", l);

    bind_textdomain_codeset("ewe", "UTF-8");
    bindtextdomain("ewe", "/opt/btech/probe/share/locale/WA");
    textdomain("ewe");

    printf("current textdomain is \"%s\"\n", textdomain(NULL));
    printf("current base directory is \"%s\"\n", bindtextdomain(textdomain(NULL), NULL));
    printf("current LC_MESSAGES locale is \"%s\"\n", setlocale(LC_MESSAGES, NULL));
    printf("gettext(\"Error\") ==> \"%s\"\n", gettext("Error"));

    return 0;
}

这是动态编译时的输出(对于目标或主机):

setlocale() returned "(null)"
current textdomain is "ewe"
current base directory is "/opt/btech/probe/share/locale/WA"
current LC_MESSAGES locale is "C"
gettext("Error") ==> "Error"

编辑:在我的主机(x64 Linux)上将测试二进制文件编译为静态也可以使其工作,因此静态编译有一些特别之处。

附加问题:我可以强制 gettext 直接加载特定的 mo 文件吗?基本上我想替换bindtextdomain(),它接受一个文件名参数。

编辑 2:所以,我最终发现 this post 说我可以让 gettext() 加载任何翻译,只要我有一个有效的 setlocale() 首先调用。因此,我当前的解决方法是实际生成一个仅包含 en_US 语言环境的 /usr/lib/locale/locale-archive,调用 setlocale(LC_MESSAGES, "en_US"); setenv("LANGUAGE", "fake");,最终加载正确的消息目录。仍然感觉像一个丑陋的解决方法,我仍然不明白为什么没有它静态链接也能工作。

【问题讨论】:

  • setlocale(LC_MESSAGES, "");与适当的环境变量一起使用不是更好吗?
  • 这是一个嵌入式系统,我在没有设置环境变量的情况下运行。当程序启动时,我从配置文件中加载要使用的语言(即要设置的语言环境)。
  • 首先执行setenv("LC_MESSAGES", "fake") 并用setlocale(LC_MESSAGES, "") 替换setlocale 调用仍然与直接执行setlocale() 调用相同。

标签: c localization glibc setlocale


【解决方案1】:

我遇到了类似的问题(我对根文件系统没有太多控制权的嵌入式系统),我发现这种解决方法可行:

  • 将所有翻译放入/&lt;mydir&gt;/lang/
  • SYS_LC_MESSAGES 可以用localedef 生成。我从系统上的 C 语言环境中制作了一个并将其复制到每个目标目录

    mkdir output
    localedef -f UTF-8 -i /usr/share/i18n/locales/C  output/mylocale
    cp output/mylocale/LC_MESSAGES/SYS_LC_MESSAGES <mydir>/lang/ENG/LC_MESSAGES/
    
  • LOCPATH 设置为&lt;mydir&gt;
  • 使用strace 调试事物以查看它试图打开哪些文件。

这是我的最终文件布局:

<mydir>
├── lang
│   ├── ENG
│   │   └── LC_MESSAGES
│   │       ├── SYS_LC_MESSAGES
│   │       └── mac.mo
│   ├── FRE
│   │   └── LC_MESSAGES
│   │       ├── SYS_LC_MESSAGES
│   │       └── mac.mo
│   ├── GER
│   │   └── LC_MESSAGES
│   │       ├── SYS_LC_MESSAGES
│   │       └── mac.mo
│   ├── ITA
│   │   └── LC_MESSAGES
│   │       ├── SYS_LC_MESSAGES
│   │       └── mac.mo
│   ├── SPA
│   │   └── LC_MESSAGES
│   │       ├── SYS_LC_MESSAGES
│   │       └── mac.mo

这是代码sn-p:

putenv("LOCPATH=/<mydir>/lang");
setlocale(LC_ALL, "");  
setlocale(LC_MESSAGES, "ENG");
bindtextdomain("mac", "/<mydir>/lang");
textdomain("mac");      
gettext("Hello world");  

这是一个 hack,正确的解决方案是正确生成语言环境。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    • 2020-04-05
    • 2013-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多