Все об Arch Linux

Arch Linux будет тем, что вы из него сделаете!

setlocale и каракули: почему C-программы игнорируют локаль

Если C-программа с поддержкой перевода через gettext показывает интерфейс на английском, хотя системная локаль выставлена на ru_RU.UTF-8, дело почти наверняка в одной непрописанной строке: setlocale(LC_ALL, ""). Без этого вызова в main() libc игнорирует переменные окружения LANG и LC_ALL, и программа работает в локали по умолчанию. Разбираемся, почему так происходит, как это чинить на Arch Linux, и в чём отличие «каракулей» от непереведённого интерфейса.

Что такое локаль и зачем она нужна

Локаль (locale) — это набор правил, по которым программа определяет язык, формат дат, сортировку строк, разделители чисел. В Linux локаль задаётся переменными окружения:

  • LANG — основная локаль по умолчанию
  • LC_ALL — переопределяет всё
  • LC_MESSAGES — язык сообщений и переводов

Проверить текущую локаль в Arch проще всего:

locale

В выводе увидите что-то вроде LANG=ru_RU.UTF-8. Эта переменная живёт в /etc/locale.conf, а список доступных локалей прописан в /etc/locale.gen.

Как gettext достаёт переводы

GNU gettext — стандартный механизм интернационализации в C-программах. Схема простая:

  1. Разработчик пишет строки на английском и помечает их функцией _() или gettext().
  2. Утилита xgettext вытаскивает эти строки в .po-файл (человекочитаемый каталог переводов).
  3. Переводчики заполняют .po-файлы нужными языками.
  4. msgfmt компилирует .po в бинарные .mo-файлы.
  5. В рантайме программа обращается к .mo через gettext().

Вот ключевой момент: gettext() определяет, какой .mo-файл загрузить, по текущей локали. Но текущая локаль задаётся вызовом setlocale(). Если его нет, libc не знает, какой язык показывать, и возвращает дефолтный (английский).

Почему без setlocale программа показывает не тот язык

Вот как выглядит минимальный пример с gettext:

#include <libintl.h>
#include <locale.h>

int main(void) {
    setlocale(LC_ALL, "");
    bindtextdomain("myapp", LOCALEDIR);
    textdomain("myapp");

    printf("%s\n", _("Hello, world!"));
    return 0;
}

Строка setlocale(LC_ALL, "") говорит libc: «прочитай переменные окружения и настрой локаль по ним». Без неё libc работает в «американском» режиме по умолчанию. Переменная LANG=ru_RU.UTF-8 остаётся без внимания, и gettext загружает английский .mo.

Это не баг и не особенность конкретного дистрибутива. Так работает стандарт POSIX: программы не обязаны читать переменные окружения автоматически. Разработчик должен явно вызвать setlocale().

Реальный кейс: форк Klavaro

Klavaro — клавиатурный тренажёр с открытым кодом. В оригинальном Klavaro переводы через gettext есть, но setlocale() в main() не вызывается. Результат: даже если в Arch стоит русская локаль, интерфейс всегда на английском.

В форке Klavaro эту проблему исправили добавлением трёх строк в начало main():

setlocale(LC_ALL, "");
bindtextdomain("klavaro", LOCALEDIR);
textdomain("klavaro");

После этого программа стала считывать LC_MESSAGES из окружения и загружать русские переводы. Простая правка, а результат кардинальный.

Как проверить и настроить локаль в Arch

Шаг 1. Посмотрите текущие переменные

locale

Все переменные должны быть ru_RU.UTF-8 или хотя бы LANG=ru_RU.UTF-8. Если LANG=C или LANG=en_US.UTF-8 — программам нечего читать.

Шаг 2. Проверьте /etc/locale.conf

cat /etc/locale.conf

Там должна быть строка:

LANG=ru_RU.UTF-8

Шаг 3. Убедитесь, что локаль сгенерирована

Откройте /etc/locale.gen и раскомментируйте нужную строку (уберите # в начале):

ru_RU.UTF-8 UTF-8

Затем сгенерируйте:

sudo locale-gen

Шаг 4. Перезагрузитесь или экспортируйте

После locale-gen переменные подхватятся автоматически при следующем входе в систему. Для текущей сессии можно выставить вручную:

export LANG=ru_RU.UTF-8

Подробнее о приоритетах LC_*-переменных — в ArchWiki: Locale и Environment variables.

«Каракули» в консоли — это отдельная проблема

Часто путают два разных сценария:

  1. Программа на английском (нет перевода). Проблема в setlocale() или отсутствующих .mo-файлах.
  2. Квадратики □ вместо букв (каракули). Проблема в шрифтах.

Если в терминале вместо русских букв появляются пустые квадраты, шрифт просто не содержит нужных глифов. Решение — поставить Noto Fonts:

sudo pacman -S noto-fonts noto-fonts-cjk noto-fonts-extra

После установки пересоздайте кэш шрифтов:

fc-cache -fv

Проверьте шрифт по умолчанию:

fc-match monospace

Должен показать Noto Sans Mono или аналог с поддержкой кириллицы. Это касается и терминальных прог, и GUI. Например, в i3wm без правильного шрифта в конфиге bar тоже будут пустые ячейки вместо символов.

Частые вопросы

Можно ли использовать LC_ALL вместо LANG?

Да, но LC_ALL имеет наивысший приоритет и переопределяет всё. В setlocale(LC_ALL, "") программа берёт LC_ALL, если задан, иначе LANG. Для большинства задач достаточно правильно выставить LANG в /etc/locale.conf.

Что делает bindtextdomain()?

Указывает путь к каталогу с .mo-файлами. Стандартная иерархия: /usr/share/locale/<язык>/LC_MESSAGES/<домен>.mo. Без этой строки gettext не найдёт переводы.

Почему на серверах локаль стоит C или POSIX?

Это намеренно. Серверные программы работают с текстовыми протоколами, где локаль может сломать парсинг. C — безопасный минимум.

Как проверить, доступен ли перевод для программы?

Посмотрите, есть ли .mo-файл:

find /usr/share/locale -name "*.mo" | grep <название_программы>

Например, для gettext-домена klavaro:

find /usr/share/locale -name "klavaro.mo"

Можно ли запустить программу на другой локали?

Конечно:

LANG=en_US.UTF-8 ./myapp

Это полезно для отладки переводов или тестирования.

Заключение

Одна строка setlocale(LC_ALL, "") в начале main() — и C-программа начинает читать системную локаль. Без неё gettext просто не знает, какой язык подставить. В форке Klavaro именно это и исправили. Если интерфейс показывает не тот язык, проверяйте по порядку: locale, /etc/locale.conf, locale-gen. А если вместо букв квадратики — дело в шрифтах. Установите Noto Fonts, и проблема уйдёт.

Полезные ресурсы



Не получилось? Поможем настроить

Задай вопрос в чате — отвечаем быстро, по делу и без воды.

Читайте также

Комментарии

Загрузка…

Откроется GitHub: создайте новый issue с вашим комментарием (кнопка «Submit new issue»). После отправки обновите эту страницу — комментарий появится ниже.

Telegram Max