LC_MESSAGES到非现有语言环境的setlocale()失败

时间:2016-04-26 07:17:16

标签: c localization glibc setlocale

对于嵌入式软件项目,我正在添加对翻译的支持,因为我们运行的是嵌入式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()电话第一。因此,我目前的解决方法是实际生成仅包含/usr/lib/locale/locale-archive区域设置的en_US,调用setlocale(LC_MESSAGES, "en_US"); setenv("LANGUAGE", "fake");,最终加载正确的消息目录。仍然感觉像一个丑陋的解决方法,我仍然不明白为什么静态链接没有它。

1 个答案:

答案 0 :(得分:1)

我遇到了类似的问题(一个对根文件系统没有太多控制权的嵌入式系统),我发现此解决方法可以正常工作:

  • 将所有翻译内容放入/<mydir>/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设置为<mydir>
  • 使用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

这是代码段:

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

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