setlocale() для LC_MESSAGES для несуществующей локали завершается неудачно

Для проекта встроенного программного обеспечения я добавляю поддержку переводов, и, поскольку мы работаем со встроенным Linux, я решил использовать libc gettext(), У нас нет никаких определений локали, поэтому я только пытаюсь установить LC_MESSAGES локаль в желаемую локаль:

setlocale(LC_MESSAGES, "fake");

(Я использую имя fake с fake.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") ==> "Ḗřřǿř"

Теперь, когда я компилирую это динамически, это не работает. Ни на целевом устройстве, ни локально на моем ПК (файлы установлены одинаково). 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: Итак, я в конце концов нашел этот пост, говоря, что я могу получить gettext() загрузить любой перевод, если у меня есть действительный setlocale() позвони первым. Итак, мой текущий обходной путь заключается в создании /usr/lib/locale/locale-archive содержащий только en_US язык звонка setlocale(LC_MESSAGES, "en_US"); setenv("LANGUAGE", "fake");, что заканчивается загрузкой правильного каталога сообщений. Все еще ощущается как уродливый обходной путь, и я до сих пор не понимаю, почему статическая ссылка работает без нее.

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");  

Это хак, правильное решение - правильно генерировать локали.

Другие вопросы по тегам