【问题标题】:Erratic localization behavior of GNU gettext compared to strftime or printf与 strftime 或 printf 相比,GNU gettext 的不稳定本地化行为
【发布时间】:2016-10-28 09:51:22
【问题描述】:

我试图弄清楚为什么 GNU gettext 在本地化方面的行为与 strftimeprintf 不同。我从头到尾阅读了整个手册,但示例相当简单。我已将问题简化为一个简单的 C 程序。

任务是通过用户输入在应用程序中切换区域设置以接收本地化文件输出。

我的地区:

LANG=de_DE.UTF-8
LC_CTYPE="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_MESSAGES="de_DE.UTF-8"
LC_ALL=

考虑这个 C 示例:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <time.h>
#include <math.h>
#include "my-i18n.h"

#define PI acos(-1.0)

static char *locales[] = {
        "de_DE.UTF-8",
        "ru_RU.UTF-8",
        "he_IL.UTF-8",
        "es_ES.UTF-8",
        "C",
};

void localize(char *locale, struct tm *time) {
        printf("==============================================================\n");
        printf("Passed locale: %s\n", locale);
        char *setlocale_out = setlocale(LC_ALL, locale);
        printf("Set locale: %s\n", setlocale_out);

        char buffer[80];
        strftime(buffer, sizeof(buffer), "%c", time);
        printf("Localized time: %s\n", buffer);

        printf("Get rational, π: %f\n", PI);

        printf(_("Hello World!\n"));
        printf(_("Goodbye!\n"));
}

int main(void) {
        time_t rawtime = time(NULL);
        struct tm *time;
        localtime(&rawtime);
        time = localtime(&rawtime);

        bindtextdomain("my-i18n", LOCALEDIR);
        textdomain("my-i18n");
        int i;
        for (i = 0; i < sizeof(locales)/sizeof(locales[0]); i++) {
                localize(locales[i], time);
        }

        return EXIT_SUCCESS;
}

及其输出:

==============================================================
Passed locale: de_DE.UTF-8
Set locale: de_DE.UTF-8
Localized time: Fr 28 Okt 11:44:24 2016
Get rational, π: 3,141593
Setting LANG=de_DE.UTF-8
Hallo Welt!
Tschüß!
==============================================================
Passed locale: ru_RU.UTF-8
Set locale: ru_RU.UTF-8
Localized time: пятница, 28 октября 2016 г. 11:44:24
Get rational, π: 3,141593
Setting LANG=ru_RU.UTF-8
Hallo Welt!
Tschüß!
==============================================================
Passed locale: he_IL.UTF-8
Set locale: he_IL.UTF-8
Localized time: CEST 11:44:24 2016 אוק 28 ו'
Get rational, π: 3.141593
Setting LANG=he_IL.UTF-8
Hallo Welt!
Tschüß!
==============================================================
Passed locale: es_ES.UTF-8
Set locale: es_ES.UTF-8
Localized time: vie 28 oct 11:44:24 2016
Get rational, π: 3,141593
Setting LANG=es_ES.UTF-8
Hallo Welt!
Tschüß!
==============================================================
Passed locale: C
Set locale: C
Localized time: Fri Oct 28 11:44:24 2016
Get rational, π: 3.141593
Setting LANG=C
Hallo Welt!
Tschüß!

如您所见,strftimeprintf 完全符合我的预期,但 gettext 只考虑了我的外部语言环境。在 Google 和 SO 搜索之后,我发现bindtextdomain 和/或textdomain 必须在每个setlocale 之后执行。此外,我必须执行 putenv("LANG=...") 才能强制 gettext 工作。

修改后的C代码:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <time.h>
#include <math.h>
#include "my-i18n.h"

#define PI acos(-1.0)

static char *locales[] = {
        "de_DE.UTF-8",
        "ru_RU.UTF-8",
        "he_IL.UTF-8",
        "es_ES.UTF-8",
        "C",
};

void localize(char *locale, struct tm *time) {
        printf("==============================================================\n");
        printf("Passed locale: %s\n", locale);
        char *setlocale_out = setlocale(LC_ALL, locale);
        printf("Set locale: %s\n", setlocale_out);

        char buffer[80];
        strftime(buffer, sizeof(buffer), "%c", time);
        printf("Localized time: %s\n", buffer);

        printf("Get rational, π: %f\n", PI);

        printf("Setting LANG=%s\n", locale); 
        setenv("LANG", locale, 1);

        bindtextdomain("my-i18n", LOCALEDIR);
        textdomain("my-i18n");
        printf(_("Hello World!\n"));
        printf(_("Goodbye!\n"));
}

int main(void) {
        time_t rawtime = time(NULL);
        struct tm *time;
        localtime(&rawtime);
        time = localtime(&rawtime);

        int i;
        for (i = 0; i < sizeof(locales)/sizeof(locales[0]); i++) {
                localize(locales[i], time);
        }

        return EXIT_SUCCESS;
}

和输出:

==============================================================
Passed locale: de_DE.UTF-8
Set locale: de_DE.UTF-8
Localized time: Fr 28 Okt 11:50:33 2016
Get rational, π: 3,141593
Setting LANG=de_DE.UTF-8
Hallo Welt!
Tschüß!
==============================================================
Passed locale: ru_RU.UTF-8
Set locale: ru_RU.UTF-8
Localized time: пятница, 28 октября 2016 г. 11:50:33
Get rational, π: 3,141593
Setting LANG=ru_RU.UTF-8
Привет мир!
Пока!
==============================================================
Passed locale: he_IL.UTF-8
Set locale: he_IL.UTF-8
Localized time: CEST 11:50:33 2016 אוק 28 ו'
Get rational, π: 3.141593
Setting LANG=he_IL.UTF-8
Hello World!
Goodbye!
==============================================================
Passed locale: es_ES.UTF-8
Set locale: es_ES.UTF-8
Localized time: vie 28 oct 11:50:33 2016
Get rational, π: 3,141593
Setting LANG=es_ES.UTF-8
¡Hola mundo!
¡Adios!
==============================================================
Passed locale: C
Set locale: C
Localized time: Fri Oct 28 11:50:33 2016
Get rational, π: 3.141593
Setting LANG=C
Hello World!
Goodbye!

有人能解释一下这种“奇怪”的行为吗?

编辑:我在邮件列表中提出了它:http://lists.gnu.org/archive/html/bug-gettext/2016-11/msg00002.html

【问题讨论】:

    标签: c localization gettext


    【解决方案1】:

    在调用setlocale() 之后需要调用bindtextdomain() 的主要原因是缓存

    如果每个gettext() 调用都要检查环境变量,那会很慢。也许今天的机器不会引起注意,但它过去很重要。相反,在您发出 bindtextdomain() 调用时会完成(区域设置)查找,并缓存结果以提高效率。

    在实践中,有.. 疣。特别是,LANGUAGE 环境变量会影响每个gettext() 调用。此外,gettext 确实 not 支持线程特定的语言环境(通过uselocale() GNU 扩展)——在 Linux 中,当使用 glibc 2.4 或更高版本(2006 年发布)时,以及自 2013 年以来在 macOS/Mac OSX 中或附近。 (编辑:在此之前我开始使用 gettext 方式,只是没能保持最新状态;我做了假设我不应该有。道歉。)因为 gettext 的初始版本早在 1990 年,并且从那时到现在,很多东西都发生了变化,你不妨考虑一下这个历史包袱,甚至是一个错误。

    【讨论】:

    • 嗯..但是putenv 呢。即使使用bindtextdomain,如果不调用putenv,它也不起作用。文档对此并没有真正的帮助。
    • @Michael-O:我认为这是一个错误;我依稀记得几年前在某个地方看到过一些关于它的讨论(可能是bug-gettext 邮件列表,或者一个 musl 邮件列表),与我提到的那些实际问题有关。顺便说一句,为什么不用setenv("LANG", locale, 1) 而不是putenv()
    • 这只是我在 SO 上找到的示例代码。我会改变我的问题。谢谢。
    • 您是否建议写信给邮件列表?
    • 不,不是真的,因为已知序列setenv("LANG", locale, 1); setlocale("LC_ALL", locale); bindtextdomain(...); textdomain(...); 可以工作,并且如果应用程序需要更改语言环境,它们当前会执行此操作。如果您需要特定于线程的语言环境支持,则无论如何都需要重新实现 gettext() 的低级部分;由于该项目的悠久历史和跨平台性,它不太可能很快在 gettext 中实现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多