Алексей Барабанов
Кириллизация в Linux
Принято считать, что в Linux уже давно нет проблем с кириллизацией чего-либо. Эта ОС вслед за другими полным ходом движется в юникодный рай, где, как обещают, вавилонский кризис никому не грозит. Но на практике ситуация еще весьма далека от идеальной.
Тема кириллизации или «русификации», как иногда выражаются, с некоторых пор исчезла с первых позиций рейтингов интереса со стороны неофитов Linux. В нашей стране уже давно не внедряются новые кодировки. И хотя по их числу мы не превосходим народности с иероглифическими методами написания, но в европейском регионе явно ходим в рекордсменах. И вот теперь, когда, казалось бы, уже нет проблем, чтобы предложить пользователям Linux все разнообразие проверенных наработок и материалов по кириллизации, многие дистрибуторы начинают экономить, можно сказать, на самом святом. Им кажется, что надо уже сейчас заставить всех пользователей работать в универсальной кодировке UTF8 (алгоритм 8го представления символов UNICODE) и в локалях, сделанных на ее основе. Увы, в данном вопросе именно пользователи попадают между молотом и наковальней, между стремлением разработчиков дистрибутивов к псевдопрогрессу и консервативностью рынка прикладного программного обеспечения. Обсудим данный вопрос подробно на примере дистрибутива SuSE Linux 10.0 и в сравнении на примере ряда других ведущих дистрибуций.
Терминология и проблематика
Необходимость адаптации программного обеспечения к требованиям интерфейсного окружения обуславливается тем, что существует изначальная проблема определения кодировки символьных данных. Например, в UNIX-подобных системах файлы представляются последовательностью байт. Таким образом, все входные потоки данных, которые, по сути, есть файлы, тоже являются последовательностью индифферентных байтовых кодов. Как же программа априори «поймет», что за данные ей передаются?
Есть два способа: по содержимому самого потока данных, так называемый «in-band», и на основании внешнего предписания, или «out-band».
Первый способ зависит от формата, второй – диктует формат. Изначально использовался только второй способ. Например, в первых персональных компьютерах исключалось применение символьных данных в кодировках, отличных от кодовой страницы 437 (IBM Codepage 437). Использование данной кодовой таблицы было закреплено аппаратно, то есть внешним путем. С распространением компьютерной технологии в регионах, где приняты иные требования на представление символьных данных, была определена процедура так называемой локализации, или модификации программного обеспечения для использования региональных стандартов. Но данный путь оказался слишком трудозатратным. И тогда было произведено функциональное выделение всех национально-зависимых программных компонентов так, чтобы можно было настраивать требуемую локализацию динамически. Естественно, данная процедура всецело определялась платформой. Так, в частности, на обсуждаемой платформе GNU/Linux локализация управляется единым образом на основе так называемых «локалей» (locale) с помощью системной библиотеки glibc.
Второй способ, «in-band», подразумевает, что, получив некоторые символьные данные, программа самостоятельно сумеет определить их кодировку, пользуясь лишь информацией из входного потока. Например, кодировка может указываться в самом файле или в начале потока данных. Такой способ принят во многих внутренних форматах, поддерживаемых текстовыми редакторами, в формате HTML для этого используются специальные тэги, а в протоколе HTTP специальные опции и так далее. Но, несомненно, самым универсальным является способ использования кодировки такого размера (разрядности), чтобы она смогла содержать в себе все возможные символьные комбинации. Так возникла идея кодировки UNICODE (Unicode standard ISO 10646, или Universal Character Set, или UCS) и её более компактной версии UTF-8 [1]. Казалось бы, проблема решена, да не тут-то было! Универсальная кодировка не отправила использование локалей в прошлое, а лишь добавила проблем, поскольку увеличила их число!
Локали в Linux
Итак, в программном окружении определяется понятие локали как совокупности данных об используемых языковых и национальных особенностях среды исполнения. Именно пользуясь параметрами локали, программа «понимает» правильным образом символьные входные данные и выводит свои отчеты в правильных кодировках. Здесь, в самом определении, заложена некая условность, приводящая к неверному пониманию сущности происходящего. Бытует мнение, что из привязки локали к процессу следует легкость манипулирования её настройкой. Мол, если локаль передается процессу из окружения, то ничего нет проще, как установить любую локаль прямо перед запуском. Например, в Linux используются для этого переменные окружения LC_* и LANG. Эти переменные формируются в процессе отработки профиля пользователя и далее передаются всем порожденным процессам. Вот как все просто! Если в системе в базе локализации присутствует нужная локаль, то, указав ее в окружении, мы можем заставить работать с нею любой процесс. Увы, нет! Это со всех сторон наивный взгляд. Чтобы понять свойства локали, давайте взглянем на процесс ее образования (см. врезку «Локали и их образование»).
Кодировка, включенная в локаль, является тем самым ключевым параметром, позволяющим методом «outband» указать на способ расшифровки символов. То есть программа еще и различает все входные данные с использованием кодировки локали и, кроме того, в той же кодировке выводит все сообщения. Ну, положим, если для вывода используются объекты среды, которые наследуют локаль программы, то еще можно надеяться, что при некоторых условиях они будут адекватно воспринимать и отображать кодировку данной локали. Но в отношении входных данных это не всегда верно. Точнее, в отношении входных данных не работает обратная логика. Если локаль процесса, использующего входные данные, не совпадает с локалью процесса, их породившего, то, скорее всего, ничего хорошего не выйдет.
Самый простой пример такой ситуации, когда программа использует ранее накопленные данные, где тип используемой кодировки определяет информацию – это имена файлов, индексы в структурах БД, базы служебных сообщений, сохраненные из Интернета документы. Вы наблюдали на экранах компьютеров «кракозябры» в сообщениях программ, которые в 90% случаев работали вполне адекватно? Вам приходилось вместо имен файлов видеть всякую «бнопню»? Это именно те случаи, когда программа воспользовалась строковой константой, подготовленной в другой локали, или пыталась прочесть символьные данные, созданные с использованием иной локали. Даже точно «угадав» локаль исходных данных, то есть, казалось бы, сводя проблему к тривиальной перекодировке, не всегда удается достичь успеха. Пример тому – данные, упорядоченные или проиндексированные, в соответствии с алфавитным порядком для некоторой локали. Здесь перекодировка не даст успеха, а сортировка будет уже не только трудоемкой, но и даже невозможной, если используются разделяемые данные, что характерно для совместной сетевой работы.
Ну хорошо, локаль – это типичное указание «out-band» кодировки. Примение UTF-8 должно, по идее, избавить от дополнительного указания на локаль, так как эта кодировка допускает определение локализации на основании самих данных. Но, увы, лишь в теории. Локаль, безусловно, указывает на единственную используемую в ней кодировку, а вот кодировка может использоваться в разных локалях. И чем универсальнее эта кодировка, тем больше выбор возможных локалей. То есть в случае абсолютного универсального идеала кодирования информации UTF-8 мы вообще лишились возможности по характеру данных сделать заключение о том, в какой локали эта информация представлена!
Вот и получается, что кажущаяся легкость манипулирования локалью на самом деле не соответствует действительности. Таким образом, локаль, кроме национальной базы стандартов, включает в себя тип кодировки внешней среды, который от процесса не зависит, да и локаль не определяет! И, значит, для того чтобы обрабатывать широкий спектр входных данных, надо иметь в системном окружении весь необходимый перечень локалей для возможных кодировок, и все сопутствующие данные для построения правильной среды исполнения программы.
Вот о сопутствующих данных далее и поговорим, но сначала проверим, какие локали нам предлагаются в SuSE Linux.
Локали в SuSE Linux 10.0
Выполним установку системы по умолчанию, лишь указав, что принимается русский язык, и в результате получим следующую настройку локализации:
> locale
LANG=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=
|
Как можно убедиться, предлагается работать в локали ru_RU в многобайтной кодировке UTF-8. При этом в системе определены следующие варианты настройки:
> locale
ru_RU
ru_RU.koi8r
ru_RU.utf8
|
Здесь уже должно быть понятно, что представления об используемых в нашей стране локалях у разработчиков SuSE Linux далеки от реальности. В качестве эталонного примем мнение авторов документа [2], прошедшего проверку опытом. Кстати, из указанного источника можно почерпнуть дополнительный комментарий, который по причине форматных ограничений не вошел в настоящий текст.
Поправим в немецком дистрибутиве SuSE Linux состав локалей, которые необходимы для работы российского пользователя, следующим скриптом, запускаемым от root:
#!/bin/sh
LOCALE=/usr/lib/locale
LP=ru_RU
LD=$(which localedef)
[ "1$LD" == "1" ] && { echo localedef not found ; exit -1 ; }
LD="$LD -c -i $LP"
[ "1$(locale -a | grep ^$LP | grep utf8)" == "1" ] && $LD -f UTF-8 $LP.UTF-8
[ "1$(locale -a | grep ^$LP | grep UTF-8)" == "1" ] && ln -sf $LOCALE/$LP.utf8 $LOCALE/$LP.UTF-8
[ "1$(locale -a | grep ^$LP | grep koi8r)" == "1" ] && $LD -f KOI8-R $LP.KOI8-R
[ "1$(locale -a | grep ^$LP | grep KOI8-R)" == "1" ] && ln -sf $LOCALE/$LP.koi8r $LOCALE/$LP.KOI8-R
[ "1$(locale -a | grep ^$LP | grep cp1251)" == "1" ] && $LD -f CP1251 $LP.CP1251
[ "1$(locale -a | grep ^$LP | grep CP1251)" == "1" ] && ln -sf $LOCALE/$LP.cp1251 $LOCALE/$LP.CP1251
[ "1$(locale -a | grep ^$LP | grep iso88595)" == "1" ] && $LD -f ISO-8859-5 $LP.ISO-8859-5
[ "1$(locale -a | grep ^$LP | grep ISO-8859-5)" == "1" ] && ln -sf $LOCALE/$LP.iso88595 $LOCALE/$LP.ISO-8859-5
[ "1$(locale -a | grep ^$LP | grep cp866)" == "1" ] && $LD -f IBM866 $LP.CP866
[ "1$(locale -a | grep ^$LP | grep CP866)" == "1" ] && ln -sf $LOCALE/$LP.cp866 $LOCALE/$LP.CP866
[ "1$(locale -a | grep ^$LP | grep maccyrillic)" == "1" ] && $LD -f MAC-CYRILLIC $LP.MAC-CYRILLIC
[ "1$(locale -a | grep ^$LP | grep MAC-CYRILLIC)" == "1" ] && ln -sf $LOCALE/$LP.maccyrillic $LOCALE/$LP.MAC-CYRILLIC
И тут же проверим результат:
> locale -a | grep ^ru_RU
ru_RU
ru_RU.cp1251
ru_RU.CP1251
ru_RU.cp866
ru_RU.CP866
ru_RU.iso88595
ru_RU.ISO-8859-5
ru_RU.koi8r
ru_RU.KOI8-R
ru_RU.maccyrillic
ru_RU.MAC-CYRILLIC
ru_RU.utf8
ru_RU.UTF-8
|
Полученные локали позволят работать с кириллицей в соответствии с национальным стандартом ru_RU, используя кодировки UTF-8, KOI8-R, CP1251, ISO-8859-5, CP866 и MAC-CYRILLIC.
Некоторого пояснения требует сосуществование полного и сокращенного (в оригинале mangled – «порубленного») наименования локали, например, ru_RU.KOI8-R и ru_RU.koi8r. В принципе достаточно лишь сокращенного. Полное имя локали, указанное в переменных окружения, будет преобразовано к сокращенному в процессе работы. Но, учитывая мнение [1] и тот факт, что в отечественных дистрибутивах, например в ALT Linux (до версии 3.0), принято использование полного наименования локали даже в директории размещения (/usr/lib/locale), вероятно в расчете на независимость и оригинальность российских программистов, создадим символьные ссылки с полными именами на директории с базами локалей, которые были построены localedef.
Как видите, в недрах SuSE Linux заложен большой потенциал – расширение базы локализаций произошло без загрузки каких-либо дополнительных файлов кроме дистрибутивных.
В работающей системе SuSE Linux локаль устанавливается единым образом на основании переменной RC_LANG, размещенной в файле /etc/sysconfig/language. Ранее, сразу после присвоения данной переменной нужного значения, например того же ru_RU.UTF-8, следовало выполнить:
# SuSEconfig -module profiles»
для модификации служебных файлов профилей, формирующих окружение командной оболочки. Но теперь в этом нет необходимости, так как профили формируются универсальными скриптами /etc/profile.d/*.sh, среди которых lang.sh непосредственно читает /etc/sysconfig/language и все настраивает интерактивно, то есть в процессе запуска командной оболочки, и потом передается всем порожденным процессам. Таким образом, после модификации переменной RC_LANG достаточно перегрузить пользовательскую сессию или запустить дополнительную через «su -», чтобы начать работу в новой локали.
В других дистрибутивах возможен иной подход к формированию значений переменных окружения, указывающих на используемую локаль. Для того чтобы определить последовательность настройки, можно произвести контекстный поиск строки LANG в скриптах и конфигурационных файлах, расположенных в /etc.
Различные подходы к кириллизации
Вернемся к той мысли, что кроме нужной локали процесса надо обеспечить соответствующее преобразование входных и выходных данных. То есть правильную кодировку, как это упрощенно воспринимается. И здесь нам придется проанализировать, какие методы кириллизации, то есть адаптации интерфейсов для использования кириллицы, существуют.
Так как речь идет об интерфейсах, то настройки нужного преобразования кодов можно разделить на два вида: кириллизация консоли и кириллизация графической среды. Обсудим подробно консольный режим работы, как базовый по отношению ко всему остальному, поскольку именно этот режим поддерживается в ядре, а все остальные подсистемы, и X Window в том числе, работают лишь в качестве приложений.
В современных дистрибутивах Linux присутствуют два пакета интернационализации, с помощью которых настраивается кириллическая консоль. Хронологически первым является пакет kbd [3], и более поздним, порожденным как ветвь от kdb, – console-tools [4]. Долгое время ожидалось, что console-tools, содержащий множество утилит для манипуляции шрифтами и кодировками, вытеснит «простенький» kbd. Это мнение, подогреваемое русскоязычными соразработчитками console-tools (см. страницу credits на [4]) и отечественными дистрибуторами, в частности ALT Linux, «просочилось» в многочисленные руководства и учебники, например [5]. Поторопились...
По данным поисковой системы [6], пакет console-tools используется в дистрибутивах: Mandrake, Mandriva, Conectiva, ALT Linux, RedHat 6 и 7, Turbolinux, Trustix, Engarde. С первыми четырьмя все ясно, так как они явно исторически тяготеют друг к другу. А вот RedHat из перечисленных версий отметим особо!
По сведениям с того же сайта, пакет kbd применяется в дистрибутивах SuSE, Fedora, RedHat 5 и 8, WhiteBox, CentOS, ASP Linux, PLD, Aurox, StartCom, Arklinux, Openwall и все таже Conectiva. Со второго по пятый в списке представлены явные «редхатоиды» и их клоны. Очень интересно обсудить «пируэт», который совершили разработчики RedHat, перейдя на console-tools в релизах 6 и 7 и вернувшись потом снова к kbd в релизе 8. Нет, они не перегрелись в солярии. Можно предположить, что в связи с ожидаемым и скорым, как думают латентные эсперантисты, переходом в универсальную кодировку unicode (а как известно, в 8 версии RedHat Linux произошел переход на utf 8) преимущества пакета console-tools, ориентированного в основном на изощренные манипуляции с кодировками, теряют актуальность, а вот средства управления виртуальными консолями в kbd, напротив, становятся незаменимыми. Что и послужило основанием возврата к использованию kbd.
Принимая все перечисленное во внимание и учитывая, что в SuSE Linux всегда применялся и применяется сейчас пакет kbd, далее будем обсуждать именно его.
Ввод символов с консоли Linux
Консольный (иногда его называют «терминальным») драйвер состоит из двух частей. Первая отвечает за ввод с клавиатуры, а вторая за вывод на экран. То есть делится на части в соответствии с функциональным разделением самой консоли на клавиатуру и экран. Клавиатура может быть как физическим, локальным устройством, так и удаленным виртуальным. То же самое верно и в отношении экрана. Если он локальный, то соответствует экрану консоли, подключенной к компьютеру, а если виртуальный, то в последовательности преобразования может участвовать и консольный драйвер удаленной системы. Поэтому для ясности будем учитывать только локальные устройства.
Сначала разберемся, как происходит преобразование вводимых данных. Воспользуемся схемой, представленной на рис. 1.
Рисунок 1. Ввод данных с клавиатуры
Изображенная там последовательность преобразования будет иметь место в том случае, когда клавиатурный драйвер включен в режим XLATE и настроен в локали ru_RU.KOI8-R. На рисунке видно, что ключевую роль в установлении соответствия некоторой клавиши и того кода, который будет получать процесс, играет специальная таблица, называемая раскладкой. В этой таблице происходит не только назначение определенного символа каждой клавише, но и производится выбор кодировки. Иначе говоря, чтобы после нажатия клавиши «А» получить символ «А» в кодировке KOI8-R, надо использовать раскладку или таблицу, у которой в строке, соответствующей клавиатурному коду клавиши с изображением «А», и в колонке для нужного кириллического регистра клавиатуры содержится код 0xE1. Если бы требовалось получать символы в кодировке CP1251, то там должен содержаться код 0xC0. Чувствуете разницу? Не важно, что программа ожидает получать символы в соответствии с локалью. Если нужная таблица преобразований не будет загружена с помощью утилиты loadkeys в клавиатурный драйвер, то, как бы не менялась локаль, успешной работы не получится.
Второй режим работы драйвера клавиатуры, который актуален для текстового режима, это UNICODE. Как следует из названия, в этом режиме драйвер формирует на выходе коды в соответствии с UTF-8. В этом режиме все равно используется таблица преобразования, но на выходе драйвера создаются не однобайтные посылки, а строки переменной длинны. В SuSE Linux параметры, управляющие работой утилиты loadkeys, настраивающей клавиатуру, содержатся в /etc/sysconfig/keyboard. В отличие от изображенного на рис. 1 раскладка для режима по умолчанию указана как:
# grep ^KEYTABLE /etc/sysconfig/keyboard
KEYTABLE="ru1.map.gz"
Значит, используется кириллическое подмножество UTF8 (предположительно) с переключением регистров через <RightAlt> + <Shift>. Но вот проблема: кириллица вроде бы вводится, а при выводе на экран вместо знаков этого древнего алфавита отображаются лишь пустые знако-места. Заглянув в исходный текст ru1.map.gz определяем, что там производится отображение не в UTF-8, а в KOI8-R. И это недоразумение, кстати сказать, сопровождает уже несколько версий SuSE Linux подряд. Замена раскладки на ru-utf.map.gz исправляет ошибку разработчиков дистрибутива.
Здесь отметим, что перенастройка режима ввода с консоли производится системным скриптом /etc/init.d/kbd независимо от настроек локали.
Вывод символов в консоль Linux
Теперь обсудим, как производится вывод символьной информации с помощью драйвера экрана. Для этого воспользуемся схемой, представленной на рис. 2.
Рисунок 2. Вывод данных на экран монитора
На рисунке изображена последовательность преобразования в случае использования кодировки KOI8-R. Настраиваемыми параметрами являются таблица ACM (Application Character Map), предназначенная для перекодировки во внутреннее представление драйвера, таблица SFM (Screen Font Map), предназначенная для получения индекса глифа в экранном шрифтовом наборе, и собственно растровый шрифт. Два последних элемента тесно связаны и очень часто объединяются в единый файл. Файлы экранных шрифтов с именами, использующими суффиксы psfu должны содержать после растрового шрифта соответствующую таблицу перекодировки SFM. Специальная управляющая подстрока CONSOLE_MAGIC используется для активации настроенного преобразования. Подробности можно узнать в [5]. В общем все достаточно тривиально.
Как уже было сказано, по умолчанию SuSE Linux настраивается для использования UTF-8. Тогда все системные переменные, которые используются для настройки способа вывода принимают значения:
# grep ^CONSOLE_ /etc/sysconfig/console
CONSOLE_FONT="Cyr_a8x16.psfu"
CONSOLE_UNICODEMAP=""
CONSOLE_SCREENMAP="trivial"
CONSOLE_MAGIC="(K"
CONSOLE_ENCODING="UTF-8"
И здесь также отметим, что настройка вывода в консоль призводится системным скриптом /etc/init.d/kbd независимо от настроек локали. И лишь воля администратора, редактирующего соответствующий управляющий файл /etc/sysconfig/console, должна обеспечить согласование кодировок.
Настройка консоли SuSE Linux
В отличие от ранее созданных локалей для обеспечения соотвествующей настройки придется подгрузить в систему дополнительные файлы. В дистрибутивной поставке отсутствует ACM для MAC-CYRILLIC и некоторые клавиатурные раскладки. Используемые для настройки разных режимов параметры перечислены в таблице 1. Недостающие отмечены отсутствием фоновой тонировки. Эти файлы, как и все приведенные или упомянутые здесь скрипты, можно найти в архиве [7].
Таблица 1. Параметры настройки кодовых преобразований и локалей
Локаль
|
Charmap
|
Screenmap
|
Keytable
|
Encoding
|
Источник
|
ru_RU.UTF-8
|
UTF-8
|
trivial
|
ru-utf.map
|
UTF-8
|
Unicode Org
|
ru_RU.KOI8-R
|
KOI8-R
|
koi8-r_to_uni
|
ru-ms.map
|
KOI8-R
|
RFC 1489
|
ru_RU.CP1251
|
CP1251
|
cp1251_to_uni
|
ru_win.map
|
CP1251
|
MS Cyrillic
|
ru_RU.ISO-8859-5
|
ISO-8859-5
|
8859-5_to_uni
|
ru_ms-iso-8859-5.map
|
ISO-8859-5
|
SUN Cyrillic
|
ru_RU.CP866
|
IBM866
|
cp866_to_uni
|
ru_ms-ibm866.map
|
IBM866
|
IBM Cyrillic
|
ru_RU.MAC-CYRILLIC
|
MAC-CYRILLIC
|
mac-cyrillic_to_uni
|
ru_ms-mac-cyrillic.map
|
MAC-CYRILLIC
|
Apple Cyrillic
|
Вот как должен выглядеть скрипт для переключения локали и консоли в режим работы с кодировкой CP1251, выполненный в точном соответствии с таблицей 1:
# cat console2cp1251
#!/bin/sh
PREF=/etc/sysconfig
[ "1$UID" != "10" ] && { echo "you must be root!" ; exit ; }
# Console
perl -i -p -e 's/^CONSOLE_FONT=.*/CONSOLE_FONT=Cyr_a8x16.psfu/g' $PREF/console
perl -i -p -e 's/^CONSOLE_SCREENMAP=.*/CONSOLE_SCREENMAP=cp1251_to_uni/g' $PREF/console
perl -i -p -e 's/^CONSOLE_ENCODING=.*/CONSOLE_ENCODING=CP1251/g' $PREF/console
# Keyboard
perl -i -p -e 's/^KEYTABLE=.*/KEYTABLE=ru_win.map.gz/g' $PREF/keyboard
# Language
perl -i -p -e 's/^RC_LANG=.*/RC_LANG=ru_RU.CP1251/g' $PREF/language
rckbd restart
exit
Аналогичным образом строятся скрипты для переключения в другие локали (см. [7]). Результат перевода консоли в режим ru_RU.CP866 изображен на рис. 3.
Характерной особенностью является отсутствие прописной буквы «Ы» (на рисунке 3 в строке приглашения консоли), которая совпадает по коду со служебным символом управления кодовыми таблицами SCI (0x9B). Но в остальном все прекрасно работает.
Рисунок 3. Консоль в режиме ru_RU.CP866
Итак, потенциально возможно настроить консольный драйвер и окружение пользователя для работы в любой из перечисленных локалей. Так почему же все настойчиво предлагают работать в ru_RU.UTF-8 и так пренебрежительно относятся ко всем остальным локалям?
Использование локали приложениями
Безусловно, это самый важный вопрос. При его исследовании можно встретить как приятные открытия, так и озадачивающие. Тема эта неисчерпаема, как и сам набор возможных приложений. Для нас здесь принципиальным является тот факт, что корректное использование локалей всецело на совести разработчиков программного обеспечения. Более того, приложение может «уметь» использовать локали, но не поддерживать часть из них.
Например, X Window в используемом большинством дистрибутивов Linux варианте Xorg является, по сути, обычным приложением. Переведем SuSE Linux в локаль ru_RU.CP1251 и затем внутри работающего X Window переключимся в CP866 и запустим новую сессию konsole. Результат на рис. 4.
Рисунок 4. Запуск konsole в CP866
Получаем сразу две проблемы: во-первых, сообщение «QT: Locales not supported on X server» и, во-вторых, внутри созданного окна не наблюдается кириллицы в CP866. Первая из-за того, что нужная локаль не создана в /usr/lib/X11/locale, а вторая из-за того, что теперь надо для данного экземпляра konsole указать кодировку вручную. Вспоминаем о том, что оптимисты считают локаль свойством процесса и ... недоумеваем! Вероятно, оптимисты не входят в число авторов этих программ.
Проявляем настойчивость и получаем уже несколько иной результат на рис. 5.
Рисунок 5. Konsole в CP866 с кодировкой, указанной вручную
Обратите внимание: появилась кириллица в ответе date, корректно представлена псевдографика mc, и, самое главное, нет проблем с прописной «Ы», так как клавиатура работает в «сыром» (raw в оригинале), прозрачном режиме, и текстовая консоль не участвует в процессе вывода. Только не получится повторить то же самое в локали ru_RU.MACCYRILLIC, так как в konsole эту кодировку нельзя указать принудительно – ее просто нет. Приятно то, что варианты 14 из таблицы 1 работают в SuSE Linux в режиме X Window, как говорится, «из коробки».
Еще более интересные метаморфозы претерпевает локаль в режиме удаленной работы. Например, с помощью ssh. Если из локальной сессии, работающей в локали ru_RU.KOI8-R, подключиться к удаленному компьютеру, настроенному для работы в другой локали, то получим следующее:
alekseybb@wsalekseybb:~> 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_PAPER="ru_RU.KOI8-R"
LC_NAME="ru_RU.KOI8-R"
LC_ADDRESS="ru_RU.KOI8-R"
LC_TELEPHONE="ru_RU.KOI8-R"
LC_MEASUREMENT="ru_RU.KOI8-R"
LC_IDENTIFICATION="ru_RU.KOI8-R"
LC_ALL=
|
alekseybb@wsalekseybb:~> ssh alekseybb@192.168.0.184
Password:
Last login: Thu Mar 9 22:03:31 2006
Have a lot of fun...
|
alekseybb@wsvm02:~> 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_PAPER="ru_RU.KOI8-R"
LC_NAME="ru_RU.KOI8-R"
LC_ADDRESS="ru_RU.KOI8-R"
LC_TELEPHONE="ru_RU.KOI8-R"
LC_MEASUREMENT="ru_RU.KOI8-R"
LC_IDENTIFICATION="ru_RU.KOI8-R"
LC_ALL=
|
alekseybb@wsvm02:~> grep ^RC_LANG /etc/sysconfig/language
RC_LANG=ru_RU.MAC-CYRILLIC |
Как видно из протокола сеанса, командная оболочка запустилась в локали клиента. Произошло это благодаря тому, что в параметрах сервера ssh указано экспортировать переменные среды клиента:
wsvm02:~ # grep ^AcceptEnv /etc/ssh/sshd_config
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL
|
А в параметрах клиента, аналогично, передавать переменные среды на удаленную сторону:
wsalekseybb:~ # grep ^SendEnv /etc/ssh/ssh_config
SendEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
SendEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
SendEnv LC_IDENTIFICATION LC_ALL
|
Но если попытаться подключиться к компьютеру в локали ru_RU.MAC-CYRILLIC с помощью PyTTY из-под MS Windows, то ничего хорошего не получится, так как там нет возможности ни передать переменные программной среды, ни прямо указать перекодировку с MAC-CYRILLIC.
Надо заметить, что в приложениях, не поддерживающих POSIX-локали, бывают вообще странные ситуации, вроде той, что сообщает wine при запуске некоторых абстрактно написанных программ:
fixme: msvcrt:MSVCRT_setlocale : Codepage only locale not implemented |
Дословно: локаль, определяемая только кодовой таблицей, не реализована. В свете того, что по кодировке символьного потока вообще нельзя установить тип локали, приведенное выше сообщение можно понимать, как своего рода юмор.
Подведем итоги
В общем случае не представляется сложным настроить рабочую станцию GNU/Linux в любой из желаемых локалей. Как было продемонстрировано на примере SuSE Linux 10.0, который не является кириллически толерантным, что указано в предупреждении инсталлятора, даже такой дистрибутив поддается настройке. Казалось бы, отечественные дистрибутивы должны все описанное в статье иметь как встроенный сервис. Но этого не наблюдается. На Руси с древности повелось так, что народ учили грамоте то всякие «пришлые греки», то «ушлые варяги». Складывающаяся вокруг компьютерных кодировок ситуация следует исторической традиции. Очередные заморские гуру, в который раз предлагают отказаться от всего, что было ранее проверено и внедрено, и принять от них новую чудодейственную «пилюлю». Ожидаемо, после того как четыре зарубежные компании создали четыре взаимоисключающие способа кодирования кириллической информации, полностью игнорируя ту, что была создана независимыми отечественными разработчиками, в среде пользователей должен сформироваться иммунитет к языковым авантюрам. Тем более что, как продемонстрировано выше, единого подхода к работе с локалями не наблюдается вовсе, то есть появление новой локали закономерно приведет к новым хлопотам и проблемам с программами. Но лишь один из ведущих российских разработчиков – ASPLinux – признает сложившуюся ситуацию и предлагает набор локалей и кодировок на выбор (все из перечисленного, кроме экзотической кодировки Mac и устаревшей cp866, в версии 10 ASPLinux). А вот дистрибуция ALT Linux, копируя западный подход, с версии 3.0 полностью переходит в ru_RU.UTF-8. Причем рекомендации по «откату» на привычную KOI8R выкладываются в Сеть синхронно с появлением этой искусственно созданной «проблемы» [8]. Вероятно, тщательная кириллизация Linux не только стала казаться неактуальной для некоторых пользователей, но даже и не считается обязательным качеством национальной продукции у некоторых разработчиков.
Построим сравнительную таблицу дистрибутивов, не претендуя на полноту обзора. Рассмотрим как декларированные свойства, так и латентные, подобные тем, что позволили столь эффективно произвести многие этапы кириллизации SuSE Linux. Выберем для сравнения парочку ведущих дистрибутивов зарубежного производства и уже упомянутых лидеров отечественного рынка. Зарубежные в полном формате, а отечественные в однодисковых вариантах, для того чтобы уровнять шансы. Сравнивать будем наличие системных локалей, локалей X Window, предложения установщика (в таблице колонки, озаглавленные «Уст.») и число шрифтов выбранной кодировки для X Window (например для UTF-8: «xlsfonts | grep iso10646 | wc -l»; считая, что в них уже содержатся глифы для кириллицы, что в общем случае не всегда выполняется). Полученные результаты сведём в таблицу 2.
Таблица 2. Уровень исходной кириллизации дистрибутивов
Дистрибутивы
|
SuSE Linux 10.0
|
RHEL 4 AS
|
ASP Linux 10 OEM
|
ALT Linux Compact 3.0.4
|
Критерии
|
glibc
|
Xorg
|
Уст.
|
fonts
|
glibc
|
Xorg
|
Уст.
|
fonts
|
glibc
|
Xorg
|
Уст.
|
fonts
|
glibc
|
Xorg
|
Уст.
|
fonts
|
Локали
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
UTF-8
|
Да
|
Да
|
Да
|
512
|
Да
|
Да
|
Да
|
465
|
Да
|
Да
|
Да
|
723
|
Да
|
Да
|
Да
|
59
|
KOI8-R
|
Да
|
Да
|
|
349
|
Да
|
Да
|
Да
|
228
|
Да
|
Да
|
Да
|
1027
|
Да
|
Да
|
|
78
|
CP1251
|
|
Да
|
|
0
|
|
Да
|
Да
|
0
|
Да
|
Да
|
Да
|
368
|
Да
|
Да
|
|
64
|
ISO8859-5
|
|
Да
|
|
102
|
Да
|
Да
|
|
52
|
Да
|
Да
|
Да
|
52
|
Да
|
Да
|
|
23
|
CP866
|
|
|
|
25
|
|
|
|
0
|
|
|
|
0
|
|
Да
|
|
0
|
MAC-CYRILLIC
|
|
|
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
0
|
Число полных поддержанных локалей
|
1
|
2
|
4
|
1
|
Заметно, насколько не согласована политика внутри коллективов разработчиков. Например, в SuSE инсталлятор предлагает лишь UTF-8, тогда как внутри, еще в силу привычки, все есть для безбедной работы в KOI8-R, и это при почти полном игнорировании CP1251, от качества настройки которой в нашей стране существенно зависит интеграция с MS Windows, ну а наличие шрифтов для кодировки CP866 не объяснить никакой адекватной логикой. Точно так же непонятно, зачем в ALT Linux Compact 3.0 создана локаль для X Window, соответствующая CP866, при отсутствии всего остального для этой кодировки. На этом фоне лишь ASP Linux можно считать образцом взвешенного и разумного подхода. В строке 7 указано число локалей, полностью реализованных и поддержанных сервисом. Этот параметр является киррилическим рейтингом по факту.
Так что же произошло на самом деле? Что так неоднозначно понимается создателями продукции, наполняющей внутренний рынок дистрибутивов GNU/Linux? Что их смущает?
К уже имеющим хождение в России пяти кодировкам добавилась еще одна. И всего-то! После пяти предыдущих надо появление очередной, шестой, встречать с уже натренированным навыком.
Успехов вам, читатели, в настройке собственных рабочих станций, иногда вопреки загадочному желанию разработчиков, далеких от общественных интересов!
Приложение
Локали и их образование
Стандарты локализации, применяемые в системах GNU/Linux, называются POSIX-локалями, поскольку их применение регламентируются именно этим стандартом на интерфейс между операционной системой и прикладной программой. Обычно база локалей размещается в /usr/lib/locale. В SuSE Linux указанная директория принадлежит пакету glibc-locale. Именно там ищутся описания правил национальных стандартов и прочее, что необходимо для традиционного представления данных в соответствии с принятой локалью.
И если там нужная локаль отсутствует, то ее можно скомпилировать с помощью утилиты localedef. Для этого следует вызвать утилиту с параметрами: файл описания стандартов локали и файл с описанием соответствия символов юникода (unicode) тем, что приняты для кодирования информации в данной локали. Например:
# localedef -i ru_RU -f CP1251 ru_RU.CP1251
создаст локаль ru_RU.CP1251. Название локали образуется по правилам: [язык[_территория][.кодовая_таблица][@модификатор]], в соответствии с RFC 3066. Язык указывается двумя символами в строчном регистре, согласно международному стандарту ISO 639. Территория указывается тоже двумя символами, но уже прописными, обозначающими страну по стандарту ISO 3166. Следующая компонента привязывает к локали кодовую страницу. Модификатор в кириллических локалях пока не используется.
Итак, в создании локали используются описания национальных стандартов, размещенные в /usr/share/i18n/locale/, и кодировки из деректории /usr/share/i18n/charmaps. Обе директории в SuSE Linux поставляются в пакете glibc-i18ndata. Таким образом локаль устанавливает соответствие некоторой совокупности национальных стандартов и кодировки символьных данных.
К сведению, на платформе MS Windows используется так называемый идентификатор локали (Locale Identifier – LCID), состоящий из кода языка и кода культуры. То есть кодировка исключена из локали вовсе и требует дополнительного указания, поскольку не управляется локалью. Тем самым лишний раз подчеркивается комплиментарность локали в системной среде.
Ссылки:
- Юникод. Материал в википедии – свободной энциклопедии. http://ru.wikipedia.org/wiki/UTF-8.
- RU.LINUX Frequently Asked Questions. Составитель Александр Канавин. Глава 3. Русификация – http://www.sensi.org/~ak/linuxfaq/rulinux.faq-3.html#ss3.4.
- Пакет интернационализации консоли kbd – ftp://ftp.win.tue.nl/pub/home/aeb/linux-local/utils/kbd.
- Пакет интернационализации консоли console-tools – http://lct.sourceforge.net.
- Костромин Виктор Алексеевич. «Линукс для пользователя». Электронный вариант – http://nf8.jinr.ru/~kras/kostromin/gl-11/gl_11_01.html.
- Поисковая система для rpm – http://rpm.pbone.net.
- Архив файлов к настоящей статье – http://www.barabanov.ru/arts/cyrillic/cyrillic.tgz.
- Как сменить системную локаль на KOI-8 в дистрибутиве ALT Linux Compact 3.0 – http://wiki.sisyphus.ru/utf8/MigrateToKoi8?v=1d2w.