Желающих «русифицировать» свою версию Linux масса. Количество вопросов, с этим связанных, не уменьшается. Естественно, и рецептов русификации можно найти немало. Впрочем, рецепты, как показывает практика, устаревают, и еще вчера работавшие методы сегодня приводят к ступору. Поэтому интереснее рассмотреть устройство базовых механизмов русификации. Возможно, кому-то пригодится и эта статья. Пример, который в ней используется, в принципе можно считать модельным. Однако если вам захочется порадовать свое начальство или коллег русским прямо с входа — дерзайте!

Иногда и, возможно, справедливо, меня упрекают в том, что я даю слишком мало ссылок. Эта тема из тех, когда ссылок всегда оказывается мало. Попробуем исправиться. Вариантов русификации описано достаточно много (см. список ссылок), кому-то хватает koi-8 или какого-либо другого варианта, а кому-то категорически необходимо иметь возможность переключения на ходу.

Меню

Что необходимо сделать с компьютером, для того чтобы иметь основание сказать, что операционная система Linux в достаточной степени русифицирована?

Каждый это понимает по-своему: кому-то достаточно бродить по русской части Internet, кому-то — работать с русскими текстами в том или другом редакторе, кто-то хотел бы чтобы абсолютно любую операцию можно было бы выполнить с использованием только русского языка. Попробуем уточнить список необходимых функций:

  • работа с русским текстом в рамках обычного эмулятора терминала, возможность использования русского языка при вводе команд;
  • работа с русскоязычным текстом в X Window System;
  • редактирование русскоязычных текстов;
  • получение диагностики от программ на русском языке;
  • возможность просмотра списка файлов русскоязычных каталогов Windows;
  • просмотр текстов на русском языке, подготовленных не только в Unix, но и в Windows;
  • просмотр произвольных русскоязычных сайтов;
  • прием и передача писем на русском языке;
  • возможность печати русскоязычных текстов;
  • получение информации о командах (разнообразная «помощь») на русском языке.

Список этот, очевидно, не полон. Признаюсь, что я не являюсь приверженцем полной русификации (или кириллизации) операционных систем. Начну с того, что использование англоязычных терминов зачастую более удобно из-за отсутствия дополнительных смысловых наслоений; к тому же, отсутствие «окончательного» варианта русскоязычной компьютерной терминологии иногда приводит к появлению абсолютно непонятных текстов. Да и в английской версии системы часто работать проще, чем в русской. Главное — не нарваться на продукт, который делала очень «интернациональная» команда. В моей практике был случай, когда пришлось иметь дело с программой, в которой верхнее меню было английским, большая часть интерфейса — французским, а все ошибки выдавались только на немецком.

Есть и еще один момент: иногда для обозначения тех или иных действий или объектов используются сокращения. Но попробуйте представить себе, что должна делать команда ассемблера «Ы»? А ведь это реальная команда.

На первое — щи

Попробуем разобраться не столько в конкретных методах русификации, сколько в ее механизмах. Для экспериментов используем английский/ русский вариант RedHat 6.2. (Подобная определенность необходима, поскольку в мире Linux все меняется довольно быстро. — И.О.)

Вернемся к вопросу ввода и вывода символов, который уже затрагивался в одном из выпусков рубрики (см. журнал «Открытые системы», 1999, № 11). Кроме того, конечно, нас интересует вопрос о хранении русских текстов. При этом мы можем ожидать наличия текстов в различных кодировках. Итак, остановимся на слегка модифицированной схеме:

Кнопка ->

-> последовательность сканкодов

-> код клавиши

-> набор «нормальных» (например, ASCII) символов

-> отредактированный набор символов (если участвовали клавиши редактирования)

-> буфер нашей программы

-> хранение данных

-> буфер программы

-> эмулятор терминала того или иного типа

-> изображение на экране

Рассмотрим работу с обычным эмулятором терминала.

Если требуется, чтобы текст непосредственно обрабатывался теми Unix-утилитами, которые не готовы работать с Unicode, то любой из системных вызовов (скажем, read) уже должен выдавать символы в требуемой кодировке. Аналогично дела обстоят и с выводом: любой вызов write немедленно выводит текст в «текущей» кодировке.

Загрузка конкретной кодировки выполняется посредством loadkeys прямо из /etc/rc.d/rc.sysinit. Тип кодировки задается в файле /etc/sysconfig/

keyboard:
[Вася @localhost Вася]$ 
more /etc/sysconfig/keyboard
KEYBOARDTYPE=«pc»
KEYTABLE=«ru-ms»
[Вася@localhost Вася]$ loadkeys ru-ms
Loading /usr/lib/kbd/keymaps/i386/ 
qwerty/ru-ms.kmap.gz
[Вася @localhost Вася]$

Файл ru-ms.kmap.gz задает преобразование от сканкодов к кодам, вводимым программой, и, тем самым, в частности, определяет раскладку клавиатуры. Проверить конкретный сканкод можно посредством showkey, а полный просмотр раскладки — dumpkeys.

Приведем несколько фрагментов этого файла:

# This cyrillic keymap
 of Dmitry M. Klimoff 
#  based
# on keymap of Alexey Vovenko 
# .
...
strings as usual
keycode0 =
keycode1 = Escape Escape Escape Escape
altkeycode1 =Meta_Escape
...
keycode15 = Tab Tab Tab Tab
...
keycode30 = +a
altgrkeycode30 = +0xC6
altgrshiftkeycode30 = +0xE6
...
altkeycode59 =Console_1

Эх, не очень это понятно — +0xc6 +0xe6. А что «скажет» dumpkeys:

keymaps 0-6,8-12,14
keycode1 = Escape Escape Escape Escape
altkeycode1 = Meta_Escape
altgraltkeycode1 = Meta_Escape
...
keycode29 = Control
keycode30 = +a +A +AE +ae Control_a
  Control_a Control_a Meta_a
 Meta_A Meta_a Meta_A
 Meta_Control_a 
Meta_Control_a 
...
compose ?A? ?E? to ?Ф?
compose ?a? ?e? to ?ф?

Вывод обеспечивается файлами шрифтов, которые задают вид каждого символа. Загрузка шрифтов в RedHat также происходит посредством последовательности файлов rc.sysinit ; /sbin/setsysfont ; consolechars.

Реклама у входа

Для того чтобы корректно отображался шрифт, кроме собственно загрузки шрифта, необходимо еще выдать на экран «магическую» последовательность:

echo -en «33(K» 

Некоторые считают, что лучше это делать, например, в файле .profile или его аналоге. В RedHat все подобные действия сосредоточены в файле /etc/ profile.d/lang.sh (с использованием настроек /etc/sysconf/ i18n):

echo -n -e ?33(K? > /proc/$$/fd/0

А что если разместить эту команду прямо в... rc.local? Ей там самое место. В конце работы этого скрипта генерируется файл /etc/issue, который выдается перед приглашением входа в систему. Вот именно в него мы и направим результат echo, добавим пару русских фраз, рекламку и т.п. Данное добавление, конечно, следует сделать после того, как файл /etc/issue скопируется в /etc/netissue; не станем предлагать это входящим по сети:

cp -f /etc/issue /etc/issue.net
echo >> /etc/issue
echo -ne «33(K» >>/etc/issue
echo «SHIFT ногой конечно круче»
 >>/etc/issue

Результат превосходит ожидания. Теперь вы вполне можете попробовать даже набрать свое имя на русском языке.

«Фейс-контроль»

Добавим при помощи команды типа «useradd -m Вася» нового пользователя и попробуем войти. Ой, не получилось. Увы, герой нашего времени Вася не сможет пройти фейс-контроль. Ему даже не предлагают набрать пароль! Заглянем в messages — вау, оказывается, мы использовали некорректный символ В:

Apr 9 ..:..:.. localhost
 /sbin/mingetty[..]: tty5: invalid
 character В in login name

А чего нам не хватает для нормальной жизни? Для приготовления фирменного блюда (вкус, возможно, сомнителен, но аромат... — все по-русски) нам потребуются следующие составляющие: источники mingetty — кролик, источники passwd, pam — специи и инструменты по вкусу.

Чем не понравился Вася команде mingetty? Оказывается:

} else if (!isalnum (c) && c != ?_?)
error («%s: invalid character for
 login name», tty);

— В не является букво-цифрой. Почему isalnum считает, что имеет право так себя вести? Увы, он не предупрежден своими создателями о существовании других стран. Вполне соответствует истории с www.anekdot.ru: письмо, отправленное из Соединенных Штатов в Эстонию, возвращается с ответом «Страна не найдена».

Библиотека кулинарии об интернациональной кухне

Ясно, что в общем случае у нас нет оснований надеяться, что программа запросто «согласится» работать на другом языке. Обычно, это возможно только в том случае, если разработчики программы явно предусмотрели такую необходимость. При этом мы вынуждены полагаться и на разработчиков программы, и на разработчиков системы «национализации», и на те настроечные файлы, которые необходимы для конкретного языка и, заметим, для конкретной программы. Однако вернемся к разработчикам программы. Именно они должны позаботиться об интернационализации своей программы. К сожалению, не сможем приготовить блюдо из папайи, не имея ее где-либо рядом в магазине. Это немедленно приводит к определенным ограничениям типа невозможности лексикографического сравнения строк strcmp (только для прямой эквивалентности) и т.п., т.к. сравнение подобного типа корректно только для стандартного набора ASCII.

В интернационализации (internationalisation — i18n) — помогает очередная реализация идей старого доброго NLS (более десятка лет жизни, см. например X/Open portability Guide, XSI Supplementary Definitions). Что должно учитываться в процессе интернационализации? В другом языке, возможно, должны быть другие знаки пунктуации, разделители, набор букв, обозначение валюты, времени и т.п. Что это означает для программиста? Во-первых, нельзя полагаться на собственные простые приемы программирования даже типа (t==? ?)||(t==? ?) для проверки «на пустоту»; во-вторых, необходимо использовать только стандартные функции для этих целей; в-третьих, некоторые стандартные функции, естественно, для разных языков/кодировок будут вести себя различно, а некоторые уже по самому описанию не интернациональны и должны заменяться на другие. Список затрагиваемых функций широк. Ясно, что в него должен входить и printf, и tolower, средства работы с регулярными выражениями и т.п.

Итак, вернемся к нашему кролику. Авторы воспользовались вполне корректным приемом, т.е. стандартным макросом isalnum. Чего же не хватает для счастья? Для счастья не хватает маленького включаемого файла и процедуры инициализации в начале программы. Воспользуемся более коротким примером:

/* #include  — этого
 еще нет */
#include 
int main(int argc, char **argv)
{
/* setlocale(LC_ALL, «»); 
— пока заклинание отключено */
printf((argc>1 &&
 isalnum(argv[1][0]))?«yes
»:«no
»);
}

Эта программа упорно предполагает, что буква «В» из Вася ни буквой, ни цифрой не является. Однако стоит раскомментировать пару строк и, при соответствующей установке внешних переменных, наша программа готова сменить гнев на милость — не только «В», но и «я» становятся буквами.

Чего же нового мы привнесли своими действиями? Добавление setlocale (установить «локаль») указывает программе необходимость инициализировать ряд таблиц, в частности, тех, что используются для isalnum, в соответствии с требованиями языка.

Что будет, если мы попытаемся вставить данные две строчки в mingetty? Увы, сразу это не сработает. Почему опять неудача?

Карта вин

Что же нам надо от внешних переменных? Проверить текущую «локаль» можно командой locale:

[root@localhost profile.d]# locale
LANG=ru_RU.KOI8-R
LC_CTYPE=«ru_RU.KOI8-R»
LC_NUMERIC=«ru_RU.KOI8-R»
LC_TIME=«ru_RU.KOI8-R»
LC_COLLATE=«ru_RU.KOI8-R»
LC_MONETARY=«ru_RU.KOI8-R»
LC_MESSAGES=«ru_RU.KOI8-R»
LC_ALL=
[root@localhost profile.d]#

Это же сколько уже понаписали. А для изменений тоже столько?

[root@localhost sysconfig]# 
locale -a | fgrep ru
ru
ru_RU
ru_RU.KOI8-R
ru_UA
russian
[root@localhost sysconfig]#
 LANG=russian
[root@localhost sysconfig]# locale
LANG=russian
LC_CTYPE=«russian»
LC_NUMERIC=«russian»
LC_TIME=«russian»
LC_COLLATE=«russian»
LC_MONETARY=«russian»
LC_MESSAGES=«russian»
LC_ALL=
[root@localhost sysconfig]#

Как видите, изменения делаются за один шаг. Правда, не следует забывать, что перечисленные ru могут иметь различные кодировки, соответственно, могут быть нужны другие шрифты и так далее, т.е. нужен вызов загрузчиков для клавиатуры и шрифтов. Общий список всех локалей можно получить по команде locale -a.

Изменений «локали» оказывается достаточно для того, чтобы масса программ сменила свое поведение. Программы явно умнеют и могут даже ответить на достаточно интересные вопросы типа:

root@localhost /root]# locale -c
 currency_symbol
LC_MONETARY
руб

В аналогичном стиле можно узнать текущее представление о том, как должен выглядеть положительный/отрицательный ответ на вопрос, представление времени и т.п.

Правда, для работы с некоторыми программами для гарантии лучше не пытаться использовать перечисление:

tr [a-zа-я]      [A-ZА-Я]

Эта запись выглядит вполне логично для вызова программы, которая должна преобразовать все символы из маленьких в большие, но, к сожалению, это не обязано работать. Более правильная запись такова:

tr [:lower:]      [:upper:]

Где лежит нужная нам информация? Попросим сказать нам это команду localedef —help:

usr/share/i18n/charmaps,
 /usr/share/i18n/repertoiremaps,
 /usr/share/locale:/usr/share/i18n

Таким образом, проблема с mingetty заключается в том, что установка для языка отсутствует. Нам достаточно задать LANG. Вот только где его задать? mingetty запускается прямо init. Соответственно, ни установки из rc, ни lang.sh, который и вовсе обрабатывается после входа в систему, нам не помогут. Можно вспомнить предыдущий номер журнала и файл /etc/initscipt:

export LANG=ru_RU.KOI8-R
eval exec «$4»

После создания подобного файла Васю допускают до набора пароля.

Если же в файле /etc/passwd или /etc/shadow у Васи обнаружится разумный пароль (можно скопировать редактором от кого-либо), то Вася сможет войти в систему.

Проблемы с оркестром

Ну вот, теперь мы добились возможности войти под русским именем. У нас еще есть определенные проблемы. Одну из проблем, связанную с возможностью работать с оным именем по сети, мы решать не будем. В принципе никто не мешает иметь два имени для входа — нормальное русское и псевдоним на английском (что-нибудь в стиле zq1_rtZ8). Есть и другая проблема — почему это я предложил скопировать пароль редакторам? Увы, нам не удастся воспользоваться командой passwd:

[root@localhost /root]# passwd Вася
Changing password for user Вася
passwd: User not known to the underlying
 authentication module
[root@localhost /root]#

Снова заглядываем в /var/adm/messages (уровень диагностики можно менять ключом debug в файле /etc/pam.d/passwd и т.п.):

Apr 10 ..:..:.. localhost PAM_pwdb[..]: 
bad username [Вася] 

Итак, наш оркестр исполнил «pam, pam-param, pam, pam ,pam». PAM тоже, естественно, никто не русифицировал. Увы, увы.

«Накалываемся» же мы точно на той же строчке, т.е. isalnum, но в pam_support.-c. Этот включаемый файл используется для файла библиотеки pam_pwdb.c

Беглый просмотр (читай find) включаемых файлов, существенных для pam, показывает, что... все не так плохо. Сам pam скомпилирован с включаемым файлом locale.h. Сразу получаем следствие: достаточно вставить setlocale в passwd. Проверяем — все работает.

Федя, дичь

Что нужно для того, чтобы работала с русским языком совершенно конкретная программа?

Опять же надо определиться с терминологией (см. выше). Кроме того, надо учесть наличие определенных требований к поведению программы. Пример? Ну, скажем, порядок сортировки должен изменяться в зависимости от выбранной кодировки. Каков абсолютный минимум, который мы хотели бы получить от программы? Ясно, что этот минимум состоит в том, что программа должна так или иначе позволять работать с русским текстом. Например, для bash это требование означает, что мы можем вводить команду на русском языке, формируя комбинации типа «echo Это важно!». Почему bash может не работать? Ответ очевиден. Он тоже плохо представляет себе необходимость обрабатывать «запредельные» для ASCII символы. Если заглянуть в man для bash, то обнаруживаем там специальные параметры для работы с подобными символами. Там же находим и рекомендации по их расположению в файле INPUTRC. Соответственно в /etc/inputrc встречаем:

set meta-flag on
set input-meta on
set convert-meta off
set output-meta on

P.S. Команда «Ы» — это «вЫвод».

Ссылки

[1] Библиотека Мошкова — www.lib.ru/CYRILLIC

[2] Максим Чертков — www.opennet.ru/prog/sml/21.shtml

[3] Русскоязычная документация по Linux — linux.org.ru

[4] Евгений Балдин, по мотивам аналогичного документа Александра Беликова The Linux Cyrillic HOWTO (rus) (можно найти на сайте linux.org.ru)

[5] Зубков С.В. Linux. Русские версии. — М.: ДМК Пресс, 2000

[6] Костромин В.А. Шрифты и их использование в Linux. Byte Россия, 2000, № 12

[7] X/Open portability Guide, XSI Supplementary Definitions, X/Open Company, Ltd., 1988

Если тема русификации Linux, как вам кажется, заслуживает продолжения, пишите по адресу ioblakov@bigfoot.com.

Игорь Облаков — технический директор ЗАО РАПАС (г.Москва)