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");
Это хак, правильное решение - правильно генерировать локали.