与strftime或printf

时间:2016-10-28 09:51:22

标签: c localization gettext

我想弄清楚为什么GNU gettext的行为在本地化方面不像strftimeprintf。我从A到Z阅读了整本手册,但示例相当简单。我把这个问题简化为一个简单的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!

有人可以解释这个&#34;奇怪的&#34;行为?

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

1 个答案:

答案 0 :(得分:0)

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

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

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