Программирование в стандарте POSIX

Языково-культурная среда

Разбить на страницы
Показывать лекцию целиком

Основные понятия и объекты

Согласно стандарту POSIX-2001, языково-культурная среда – это часть пользовательского окружения, зависящая от языковых и культурных соглашений .

Настройка на определенную языково-культурную среду представляет собой процесс формирования в системе совокупности данных, специфичных для поддержки конкретных естественных языков, местных настроек и кодировок символов. Иногда подобный процесс называют также локализацией, в противоположность интернационализации – процессу подготовки приложений, способных настраиваться на различные языково-культурные среды.

Языково-культурная среда формируется из данных, принадлежащих нескольким именованным категориям. Каждая категория управляет определенными аспектами поведения компонентов системы. Имена и назначение категорий соответствуют следующим переменным окружения.

LC_CTYPE

Классификация символов, преобразование регистра (верхний/нижний) символов.

LC_COLLATE

Порядок алфавитного сравнения символов.

LC_MONETARY

Форматирование денежных величин.

LC_NUMERIC

Форматирование числовых (но не денежных) величин.

LC_TIME

Форматы даты и времени.

LC_MESSAGES

Форматы информационных и диагностических сообщений и интерактивных ответов.

Категории подразделяются на более мелкие элементы, средством именования которых служат ключевые слова. Например, в категорию LC_CTYPE входят элементы с именами alpha (буквы), digits (цифры) и т.п.

Как правило, значениями перечисленных переменных окружения являются абсолютные маршрутные имена файлов, содержащих соответствующие определения. Другой вариант (если значение не начинается с символа / ) – имя среды.

Расширение XSI стандарта POSIX-2001 уточняет один из возможных форматов имени языково-культурной среды:

язык[_территория][.кодировка][@модификатор]

Примеры: ru_RU.koi8r, ru_UA.

Модификатор позволяет выбрать конкретный вариант данных о среде в пределах отдельной категории (скажем, словарный порядок сравнения в категории LC_COLLATE ). Пример: LC_COLLATE=De_DE@dict.

В каждой реализации определены одна или несколько языковокультурных сред. Поддержка POSIX-среды с именами-синонимами "POSIX" и "C" является обязательной. Кроме того, возможно создание новых сред, если в системе определен конфигурационный параметр _POSIX2_LOCALEDEF.

Стандартом POSIX-2001 предусмотрены две "собирательные" переменные окружения, определяющие сразу все (или почти все) категории языково-культурной среды.

LC_ALL

Значение этой переменной окружения учитывается в первую очередь.

LANG

Значение этой переменной окружения учитывается в последнюю очередь, если не определены значения переменных вида LC_*.

В любой реализации некоторая среда (POSIX или иная) должна быть определена как подразумеваемая, т. е. используемая тогда, когда перечисленные выше переменные окружения не определены или имеют пустые значения. Это можно сделать, например, путем присваивания LANG=POSIX или LANG=C, после чего подразумеваемой станет POSIX-среда.

В структуре lconv, описанной во включаемом файле <locale.h>, содержатся поля, определяющие форматы денежных и числовых величин. Даже беглый взгляд на перечень этих полей позволяет составить представление о богатстве возможностей определения языково-культурной среды.

char *currency_symbol; 
/* Местное обозначение  денежной величины  */

char *decimal_point; 
/* Символ, отделяющий  целую часть         */
/* числа от дробной                        */

char frac_digits; 
/* Количество цифр в дробной               */
/* части местных денежных величин          */

char *grouping; 
/* Размеры групп цифр при                  */ 
/* форматировании чисел                    */

char *int_curr_symbol; 
/* Международное обозначение денежной      */
/* величины                                */

char int_frac_digits; 
/* Количество цифр в дробной части         */
/* международных денежных величин          */

char int_n_cs_precedes; 
/* Признак того, что международное         */
/* обозначение предшествует отрицательной  */
/* денежной величине, а не следует за ней  */

char int_n_sep_by_space; 
/* Признак того, что международное         */
/* обозначение отделяется от отрицательной */
/* денежной величины пробелом              */

char int_n_sign_posn; 
/* Позиция знака минус в международных     */
/* обозначенияхотрицательных денежных      */
/* величин                                 */

char int_p_cs_precedes; 
/* Признак того, что международное          */
/* обозначение предшествует неотрицательной */
/* денежной величине, а не следует за ней   */
/* */

char int_p_sep_by_space; 
/* Признак того, что международное          */
/* обозначение отделяется от                */
/* неотрицательной денежной величины        */
/* пробелом                                 */

char int_p_sign_posn; 
/* Позиция знака плюс в международных       */
/* обозначениях положительных денежных      */
/* величин                                  */

char *mon_decimal_point; 
/* Символ, отделяющий целую часть денежной  */
/* величины от дробной                      */

char *mon_grouping; 
/* Размеры групп цифр при форматировании    */
/* денежных величин                         */

char *mon_thousands_sep; 
/* Символ, разделяющий группы цифр в        */
/* денежных величинах                       */

char *negative_sign; 
/* Знак минус для отрицательных денежных    */
/* величин                                  */

char n_cs_precedes; 
/* Признак того, что местное обозначение    */
/* предшествует отрицательной денежной      */
/* величине, а не следует за ней            */

char n_sep_by_space; 
/* Признак того, что местное обозначение    */
/* отделяется от отрицательной денежной     */
/* величины пробелом                        */

char n_sign_posn; 
/* Позиция знака минус в местных            */
/* обозначениях отрицательных денежных      */
/* величин                                  */

char *positive_sign; 
/* Знак плюс для неотрицательных            */
/* денежных величин                         */

char p_cs_precedes; 
/* Признак того, что местное обозначение    */
/* предшествует неотрицательной денежной    */
/* величине, а не следует за ней            */

char p_sep_by_space; 
/* Признак того, что местное обозначение    */
/* отделяется от неотрицательной денежной   */
/* величины пробелом                        */

char p_sign_posn; 
/* Позиция знака плюс в местных             */
/* обозначениях положительных денежных      */
/* величин                                  */

char *thousands_sep; 
/* Символ, разделяющий группы цифр в        */
/* числовых величинах                       */

Стандарт POSIX-2001 регламентирует средства опроса и установки характеристик языково-культурной среды, а также опроса доступного набора таких сред.

Общая логика использования средств интернационализации/локализации такова: пользователь, присваивая соответствующие значения описанным выше переменным окружения, идентифицирует свою языково-культурную среду; приложения могут опросить данные о целевой среде с помощью функции setlocale() и произвести настройку интернационализированных компонентов.

Определение языково-культурной среды

Определение языково-культурной среды помещается в файлы, к описанию формата которых мы и приступаем. Можно считать, что в подобных файлах, называемых далее файлами определения среды, содержится "исходный текст" определения, поскольку, чтобы возыметь действие, они должны быть обработаны утилитой localedef или эквивалентным средством.

Файл определения среды должен содержать определение одной или нескольких категорий, которое может сводиться к директиве copy (см. далее).

Определение категории состоит из заголовка, тела и хвостовика. Заголовок именует категорию; он должен начинаться с символов LC_ . Хвостовик строится из слова END и имени, употребленного в качестве заголовка.

Первому заголовку могут предшествовать строки, переопределяющие символ комментария (по умолчанию – # ) и управляющий символ ( \ ).

Тело категории состоит из одной или нескольких строк, содержащих идентификатор и, возможно, следующие за ним операнды. В качестве идентификатора может выступать как ключевое слово, именующее отдельный элемент языково-культурной среды, так и элемент алфавитного сравнения символов. Ключевые слова должны быть уникальными в пределах среды и не начинаться с префикса LC_.

Операндами могут быть символы, элементы сравнения и цепочки символов. Цепочки символов заключаются в двойные кавычки, операнды разделяются точками с запятой и, возможно, пробелами.

Символы и элементы сравнения представляются определенными ниже именами. Кроме того, символы могут обозначаться естественным образом или кодами; правда, при этом определение языково-культурной среды теряет в мобильности.

Имена символов заключаются в угловые скобки (например, <A>, <z> и т.п.). Такая конструкция должна в точности совпадать с именем, определенным в файле отображения символов, который специфицирует опция -f утилиты localedef.

Реализация должна поддерживать по крайней мере один набор символов и соответствующий файл отображения. Любой набор должен включать в себя все элементы из так называемого мобильного набора символов, определяемого стандартом POSIX-2001 (их имена перечислены в листинге 13.2).

Файл отображения символов имеет довольно простую структуру. Если отвлечься от несущественных деталей, он должен состоять из директив вида

<
имя_символа> <код> <комментарий>

Эти директивы заключаются между строками

Эти директивы заключаются между строками

и

END CHARMAP

Перед строкой CHARMAP может располагаться определение имени набора символов, которое задается директивой

<
code_set_name> имя

Фрагмент возможного описания набора символов KOI8-R показан в листинге 13.1.

<
code_set_name> KOI8-R
<comment_char> %
<escape_char> /
% version: 1.0
% source: RFC1489 via Gabor Kiss <kissg@sztaki.hu>
% and Andrey A. Chernov <ache@astral.msk.su>
CHARMAP
<U0000> /x00 NULL (NUL)
<U0001> /x01 START OF HEADING (SOH)
<U0002> /x02 START OF TEXT (STX)
. . .
<U0020> /x20 SPACE
<U0021> /x21 EXCLAMATION MARK
. . .
<U0030> /x30 DIGIT ZERO
<U0031> /x31 DIGIT ONE
. . .
<U0041> /x41 LATIN CAPITAL LETTER A
<U0042> /x42 LATIN CAPITAL LETTER B
. . .
<U0061> /x61 LATIN SMALL LETTER A
<U0062> /x62 LATIN SMALL LETTER B
. . .
<U044E> /xc0 CYRILLIC SMALL LETTER YU
<U0430> /xc1 CYRILLIC SMALL LETTER A
<U0431> /xc2 CYRILLIC SMALL LETTER BE
. . .
<U0429> /xfd CYRILLIC CAPITAL LETTER SHCHA
<U0427> /xfe CYRILLIC CAPITAL LETTER CHE
<U042A> /xff CYRILLIC CAPITAL LETTER HARD SIGN
END CHARMAP

Опишем теперь формат определения категорий языково-культурной среды.

Категория LC_CTYPE определяет классификацию символов, преобразование регистра и другие атрибуты. К ней относятся следующие ключевые слова: upper (прописные буквы; для POSIX-среды – 26 латинских букв верхнего регистра), lower (строчные буквы), alpha (буквы), digit (цифры), alnum (буквы и цифры), space (пробельные символы; для POSIX-среды в их число, помимо пробела, входят перевод строки, т абуляция и т.п.), cntrl (управляющие символы), punct (знаки пунктуации), graph (печатные символы за исключением пробела), print (печатные символы), xdigit (шестнадцатеричные цифры), blank (для POSIX-среды – пробел и табуляция).

С ключевым словом copy задается имя существующей среды, у которой наследуется определение категории. При наличии данной директивы другие ключевые слова недопустимы.

Ключевое слово charclass позволяет задать имена специфичных для определяемой среды классов символов. Каждый такой класс далее должен быть описан в определении категории LC_CTYPE по образу и подобию перечисленных выше стандартных классов.

Ключевое слово toupper открывает определение отображения малых букв в большие. Операнд этого слова представляет собой пару символов, заключенных в скобки и разделенных запятой. Аналогичным образом устроен элемент tolower.

В листинге 13.2 показано определение категории LC_CTYPE для POSIX-среды.

LC_CTYPE
# POSIX-среда, категория LC_CTYPE
# "alpha" по определению есть объединение элементов
"upper" и "lower"
# "alnum" по определению есть объединение элементов
"alpha" и "digit"
# "print" по определению есть объединение элементов
"alnum", "punct" и <space>
# "graph" по определению есть объединение элементов
"alnum" и "punct"
#
upper <A>;<B>;<C>;<D>;<E>;<F>;<G>;<H>;<I>;<J>;<K>;\
<L>;<M>;<N>;<O>;<P>;<Q>;<R>;<S>;<T>;<U>;<V>;<W>;\
<X>;<Y>;<Z>
#
lower <a>;<b>;<c>;<d>;<e>;<f>;<g>;<h>;<i>;<j>;<k>;\
<l>;<m>;<n>;<o>;<p>;<q>;<r>;<s>;<t>;<u>;<v>;<w>;\
<x>;<y>;<z>
#
digit <zero>;<one>;<two>;<three>;<four>;<five>;\
<six>;<seven>;<eight>;<nine>
#
space <tab>;<newline>;<vertical-tab>;<form-feed>;\
<carriage-return>;<space>
#
cntrl <alert>;<backspace>;<tab>;<newline>;\
<vertical-tab>;<form-feed>;<carriage-return>;\
<NUL>;<SOH>;<STX>;<ETX>;<EOT>;<ENQ>;<ACK>;<SO>;\
<SI>;<DLE>;<DC1>;<DC2>;<DC3>;<DC4>;<NAK>;<SYN>;\
<ETB>;<CAN>;<EM>;<SUB>;<ESC>;<IS4>;<IS3>;<IS2>;\
<IS1>;<DEL>
#
punct <exclamation-mark>;<quotation-mark>;\
<number-sign>;<dollar-sign>;<percent-sign>;\
<ampersand>;<apostrophe>;<left-parenthesis>;\
<right-parenthesis>;<asterisk>;<plus-sign>;\
<comma>;<hyphen>;<period>;<slash>;<colon>;\
<semicolon>;<less-than-sign>;<equals-sign>;\
<greater-than-sign>;<question-mark>;\
<commercial-at>;<left-square-bracket>;\
<backslash>;<right-square-bracket>;\
<circumflex>;<underscore>;<grave-accent>;\
<left-curly-bracket>;<vertical-line>;\
<right-curly-bracket>;<tilde>
#
xdigit <zero>;<one>;<two>;<three>;<four>;<five>;\
<six>;<seven>;<eight>;<nine>;<A>;<B>;<C>;<D>;\
<E>;<F>;<a>;<b>;<c>;<d>;<e>;<f>
#
blank <space>;<tab>
#
toupper (<a>,<A>);(<b>,<B>);(<c>,<C>);(<d>,<D>);\
(<e>,<E>);(<f>,<F>);(<g>,<G>);(<h>,<H>);\
(<i>,<I>);(<j>,<J>);(<k>,<K>);(<l>,<L>);(<m>,<M>);\
(<n>,<N>);(<o>,<O>);(<p>,<P>);(<q>,<Q>);(<r>,<R>);\
(<s>,<S>);(<t>,<T>);(<u>,<U>);(<v>,<V>);(<w>,<W>);\
(<x>,<X>);(<y>,<Y>);(<z>,<Z>)
#
tolower (<A>,<a>);(<B>,<b>);(<C>,<c>);(<D>,<d>);\
(<E>,<e>);(<F>,<f>);(<G>,<g>);(<H>,<h>);\
(<I>,<i>);(<J>,<j>);(<K>,<k>);(<L>,<l>);\
(<M>,<m>);(<N>,<n>);(<O>,<o>);(<P>,<p>);\(
<Q>,<q>);(<R>,<r>);(<S>,<s>);(<T>,<t>);\
<U>,<u>);(<V>,<v>);(<W>,<w>);(<X>,<x>);(\
<Y>,<y>);(<Z>,<z>)
END LC_CTYPE

Категория LC_COLLATE определяет порядок алфавитного сравнения символов для многочисленных служебных программ ( sort, uniq и т.д.), регулярных выражений, а также функций strcoll(), strxfrm() и других.

В алфавитном сравнении могут участвовать односимвольные и многосимвольные элементы. Их порядок задается весами, которых у каждого элемента может быть несколько (не более COLL_WEIGHTS_MAX ). Первый вес называется основным. Если при сравнении элементов основные веса совпали, используются соответственные пары дополнительных – до тех пор, пока либо не будет обнаружено неравенство, либо не кончатся веса.

Допускается формирование классов эквивалентности – присвоение одного основного веса нескольким элементам сравнения. Поддерживаются также отображения один-ко-многим (один символ отображается в последовательность элементов сравнения).

Разумеется, сравнение цепочек символов начинается с разбиения на элементы сравнения.

Правила алфавитного сравнения и директивы присвоения весов открываются ключевым словом order_start и завершаются директивой order_end. Подразумеваемым правилом является forward – сравнение от начала к концу цепочки. Возможен и противоположный порядок ( backward ).

После строки order_start элементы сравнения перечисляются в порядке возрастания. После элемента может быть задана последовательность его весов. Веса задаются как относительные, в виде имен других элементов и определяются позицией этих элементов при перечислении. Специальный вес IGNORE означает, что данный элемент следует игнорировать при сравнении цепочек. Многоточие в качестве элемента сравнения обозначает диапазон от предыдущего до последующего элементов. Элемент UNDEFINED представляет все, что не было указано явно.

В POSIX-среде алфавитный порядок совпадает с упорядоченностью символов в кодировке ASCII.

Категория LC_MONETARY определяет формат денежных величин. Входящие в нее элементы именуются аналогично полям приведенной выше структуры lconv. Например, после ключевого слова currency_symbol задается цепочка символов, обозначающая местные денежные величины. Если ключевое слово данной категории отсутствует в файле определения среды либо его значением является пустая цепочка или -1, значит, соответствующий элемент в языково-культурной среде неспецифицирован.

В POSIX-среде все элементы этой категории остаются неспецифицированными.

В категории LC_NUMERIC, во многом аналогичной LC_MONETARY, определяются правила и обозначения, используемые применительно к числовым данным. Здесь также действует параллель со структурой lconv.

В листинге 13.3 показано определение категории LC_NUMERIC для POSIX-среды. Собственно, специфицируется только десятичная точка.

LC_NUMERIC
# POSIX-среда, категория LC_NUMERIC
#
decimal_point "<period>"
thousands_sep ""
grouping -1
#
END LC_NUMERIC

Элементы категории LC_TIME определяют интерпретацию спецификаторов преобразований и, тем самым, поведение служебной программы date, а также функций strftime(), strptime() и некоторых других. В число поддерживаемых ключевых слов входят abday (сокращенные названия дней недели, начиная с воскресенья; элемент соответствует спецификатору %a ), day (полные названия дней недели, %A ), abmon (сокращенные названия месяцев, %b ), mon (полные названия месяцев, %B ), d_t_fmt (принятое в данной языково-культурной среде представление даты и времени, %c ), d_fmt (принятое в данной среде представление даты, %x ), t_fmt (принятое в данной среде представление времени, %X ).

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

направление:смещение:начальная_дата:
конечная_дата:имя_эры:формат_эры

Здесь направление – знак плюс или минус, смещение – ближайший к начальной дате номер года, начальная_дата – цепочка вида гггг/мм/дд (год, месяц и число начала эры), конечная_дата – год, месяц и число конца эры либо цепочки "-*" (конечной датой служит начало отсчета времени) или "+*" (конечной датой служит конец отсчета времени), формат_эры – цепочка, используемая при форматировании номеров года в эре.

Отметим, что точка отсчета может быть как самой ранней, так и самой поздней в эре. Примерами служат две христианские эры – после и до Рождества Христова, соответственно.

В листинге 13.4 показано определение категории LC_TIME для POSIX-среды.

LC_TIME
# POSIX-среда, категория LC_TIME
#
# Сокращенные названия дней недели (%a)
abday "<S><u><n>";"<M><o><n>";"<T><u><e>";\
"<W><e><d>";"<T><h><u>";"<F><r><i>";"<S><a><t>"
#
# Полные названия дней недели (%A)
day "<S><u><n><d><a><y>";\
"<M><o><n><d><a><y>";\
"<T><u><e><s><d><a><y>";\
"<W><e><d><n><e><s><d><a><y>";\
"<T><h><u><r><s><d><a><y>";\
"<F><r><i><d><a>\<y>";\
"<S><a><t><u><r><d><a><y>"
#
# Сокращенные названия месяцев (%b)
abmon "<J><a><n>";"<F><e><b>";"<M><a><r>";\
"<A><p><r>";"<M><a><y>";"<J><u><n>";\
"<J><u><l>";"<A><u><g>";"<S><e><p>";\
"<O><c><t>";"<N><o><v>";"<D><e><c>"
#
# Полные названия месяцев (%B)
mon "<J><a><n><u><a><r><y>"\
"<F><e><b><r><u><a><r><y>";\
"<M><a><r><c><h>";"<A><p><r><i><l>";\
"<M><a><y>";"<J><u><n><e>";\
"<J><u><l><y>";"<A><u><g><u><s><t>";\
"<S><e><p><t><e><m><b><e><r>";\
"<O><c><t><o><b><e><r>";\
"<N><o><v><e><m><b><e><r>";\
"<D><e><c><e><m><b><e><r>"
#
# Эквивалент AM/PM (%p) "AM";"PM"
am_pm "<A><M>";"<P><M>"
#
# Принятое в POSIX-среде представление даты и
времени (%c)
# "%a %b %e %H:%M:%S %Y"
d_t_fmt "<percent-sign><a><space><percent-sign>\
<b><space><percent-sign><e><space>\
<percent-sign><H><colon><percent-sign>\
<M><colon><percent-sign><S><space>\
<percent-sign><Y>"
#
# Принятое в POSIX-среде представление даты (%x)
"%m/%d/%y"
d_fmt "<percent-sign><m><slash><percent-sign><d>\
<slash><percent-sign><y>"
#
# Принятое в POSIX-среде представление времени (%X)
"%H:%M:%S"
t_fmt "<percent-sign><H><colon><percent-sign><M>\
<colon><percent-sign><S>"
#
# Принятое в POSIX-среде 12-часовое представление
времени (%r) "%I:%M:%S %p"
t_fmt_ampm "<percent-sign><I><colon><percent-sign\
<M><colon><percent-sign><S><space>\
<percent_sign><p>"
#
END LC_TIME

Категория LC_MESSAGES играет весьма ограниченную роль, определяя положительные и отрицательные ответы. Соответственно, она содержит два элемента – yesexpr и noexpr, значениями которых служат расширенные регулярные выражения. Для POSIX-среды данная категория описывается так, как показано в листинге 13.5.

LC_MESSAGES
# POSIX-среда, категория LC_MESSAGES
#
yesexpr "<circumflex><left-square-bracket>\
<y><Y><right-square-bracket>"
#
noexpr "<circumflex><left-square-bracket>\
<n><N><right-square-bracket>"
#
END LC_MESSAGES

Создание и опрос характеристик языково-культурной среды

Для создания целевой языково-культурной среды служит утилита localedef:

localedef [-c] [-f файл_отображения_символов] 
          [-i исходный_файл] 
          [-u имя_набора_символов]
          имя_целевой_среды

Утилита localedef преобразует рассмотренное выше определение категорий языково-культурной среды из исходного формата в целевой, пригодный для использования функциями и служебными программами, поведение которых зависит от значений переменных окружения.

Исходные данные для localedef поступают из файла с именем операнда опции -i или со стандартного ввода.

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

Аргументом опции -f является маршрутное имя файла отображения символов. После опции -u можно задать имя нестандартного набора символов.

Опция -c предписывает генерировать выходной файл даже при наличии предупреждений.

Аргумент имя_целевой_среды идентифицирует созданную языково-культурную среду. Если это имя начинается с символа /, оно интерпретируется как маршрутное имя, под которым сохраняется сгенерированное определение целевой среды (формат результатов работы localedef стандартом не специфицируется); в противном случае интерпретация имени зависит от реализации, а созданная среда становится общедоступной.

Для получения информации о языково-культурных средах предназначена служебная программа locale:

locale [-a | -m]
locale [-ck] имя ...

Будучи вызванной без аргументов, утилита locale выдает на стандартный вывод сводную информацию о среде, которая состоит из значений соответствующих переменных окружения (описанных выше, а также дополнительных, определенных, быть может, целевой системой). Результат может выглядеть так, как показано в листинге 13.6. Кавычки вокруг значений всех переменных окружения, кроме LANG, означают, что они не были установлены явным образом, а определены по значению $LANG.

LANG=ru_RU.koi8r
LC_CTYPE="ru_RU.koi8r"
LC_NUMERIC="ru_RU.koi8r"
LC_TIME="ru_RU.koi8r"
LC_COLLATE="ru_RU.koi8r"
LC_MONETARY="ru_RU.koi8r"
LC_MESSAGES="ru_RU.koi8r"
LC_PAPER="ru_RU.koi8r"
LC_NAME="ru_RU.koi8r"
LC_ADDRESS="ru_RU.koi8r"
LC_TELEPHONE="ru_RU.koi8r"
LC_MEASUREMENT="ru_RU.koi8r"
LC_IDENTIFICATION="ru_RU.koi8r"
LC_ALL=

При наличии аргументов выдается информация о некоторых (если задано ключевое слово) или обо всех (если указано имя категории) элементах категорий.

Опциям служебной программы locale приписан следующий смысл.

-a

Выдать информацию обо всех общедоступных языково-культурных средах (включая POSIX-среду ).

-m

Выдать имена доступных файлов отображения символов.

-c

Выдавать имена выбранных (прямо или косвенно, посредством ключевых слов) категорий, что делает результаты более читабельными, когда они охватывают несколько категорий.

-k

Выдавать имена и значения заданных элементов категорий.

Операнд имя может быть именем категории, элемента категории (ключевым словом) или зарезервированным словом charmap. В двух первых случаях выдается информация о поименованной категории или ее элементе, в последнем – имя файла отображения символов, который был использован при создании языково-культурной среды.

Приведем еще два примера употребления утилиты locale. Данные о категории LC_TIME, выдаваемые по команде

locale -ck LC_TIME

могут выглядеть так, как показано в листинге 13.7.

LC_TIME
abday="Вск;Пнд;Втр;Срд;Чтв;Птн;Сбт"
day="Воскресенье;Понедельник;Вторник;Среда;
Четверг;Пятница;Суббота"
abmon="Янв;Фев;Мар;Апр;Май;Июн;Июл;Авг;Сен;
Окт;Ноя;Дек"
mon="Января;Февраля;Марта;Апреля;Мая;Июня;
Июля;Августа;Сентября;Октября;Ноября;Декабря"
d_t_fmt="%a %d %b %Y %T"
d_fmt="%d.%m.%Y"
t_fmt="%T"
. . .
first_weekday=1
first_workday=1
cal_direction=1
date_fmt="%a %b %e %H:%M:%S %Z %Y"
time-codeset="KOI8-R"

Второй пример демонстрирует фрагмент интернационализированного варианта shell-процедуры, анализирующей интерактивный ответ пользователя. Предполагается, что ответ является значением переменной response.

if echo "$response" | grep -Eq 
   "$(locale yesexpr)"
then
echo "Ответ положительный"
else
echo "Ответ отрицательный"
fi

Программная установка и опрос характеристик языково-культурной среды

Мы приступаем к рассмотрению функций, предназначенных для работы с языково-культурной средой.

Функция setlocale() (см. листинг 13.9) служит для установки и/или опроса всей языково-культурной среды вызывающего процесса или отдельных категорий.

#include <locale.h>
char *setlocale (int category, 
    const char *locale);

Аргумент category задает категорию ( LC_COLLATE, LC_CTYPE, LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME ), а если его значение равно LC_ALL, то и всю среду.

Аргумент locale указывает на цепочку символов, содержащую требуемые установки для выбранной категории (категорий); в общем случае ее формат зависит от реализации. Имеется, однако, несколько предопределенных, стандартизованных цепочек.

"POSIX" или "C"

Специфицирует минимальную языково-культурную среду ( POSIX-среду ) для C-программ. При отсутствии явных обращений к функции setlocale() такая среда устанавливается по умолчанию при входе в main().

""

Специфицирует зависящую от реализации местную языково-культурную среду, определяемую значениями переменных окружения LC_* и LANG. Интернационализированная программа должна выполнять вызов вида

setlocale (LC_ALL, "");

для настройки на местную среду выполнения.

Если значением аргумента locale служит пустой указатель, функция setlocale() опрашивает текущее окружение и возвращает имя используемой языково-культурной среды или цепочку символов, ассоциированную с выбранной категорией. В противном случае в качестве результата возвращаются те же характеристики, но для новой среды. Результирующая цепочка (скопированная в отведенную прикладной программой область памяти) в сочетании с заданной категорией может быть использована в последующих вызовах функции setlocale() для восстановления соответствующей части языково-культурной среды.

Разумеется, в случае неудачного завершения вызова setlocale() результатом служит пустой указатель.

Получить детальную информацию о категориях LC_MONETARY и LC_NUMERIC текущей языково-культурной среды можно с помощью функции localeconv() (см. листинг 13.10).

#include <locale.h>
struct lconv *localeconv (void);

Структура типа lconv была описана выше. Здесь мы отметим лишь два момента.

Поля, имеющие тип char *, указывают на цепочки символов, каждая из которых (за исключением *decimal_point ) может иметь вид "" как признак того, что данное значение в текущей среде недоступно или имеет нулевую длину.

Поля типа char содержат неотрицательные числа; CHAR_MAX указывает на то, что данное значение в текущей среде недоступно.

Вызов localeconv() не может кончиться неудачей, однако следует учитывать, что возвращаемая структура может быть перезаписана последующими обращениями не только к localeconv(), но и к setlocale().

Следующая программа (см. листинг 13.11) демонстрирует использование функций setlocale() и localeconv().

#include <stdio.h>
#include <locale.h>

/* Функция опрашивает и выводит цепочки символов, */
/* ассоциированные с текущей средой и ее */
/* категориями */

void print_curr_locale_data (void) {
	/* Массив категорий для setlocale() */
	int ctgrs [] = {LC_ALL, LC_COLLATE, LC_CTYPE,
		LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME};
	/* Массив имен категорий */
	char *ctgrnms [] = {"LC_ALL", "LC_COLLATE",
		"LC_CTYPE", "LC_MESSAGES", "LC_MONETARY",
		"LC_NUMERIC", "LC_TIME"};
	unsigned int i;
	printf ("Цепочки символов, ассоциированные в
		текущей среде с\n");
	for (i = 0; i < sizeof (ctgrs) / sizeof (int); i++) {
		printf (" %s: %s\n", ctgrnms [i], setlocale
			(ctgrs [i], NULL));
	}
}

int main (void) {
	struct lconv *plc;
	/* Убедимся, что подразумеваемой является */
	/* POSIX-среда */
	printf ("Текущая языково-культурная среда:
		подразумеваемая\n");
	print_curr_locale_data();
		/* Установим местную среду */
	(void) setlocale (LC_ALL, "");
	/* Опросим и выдадим ее характеристики */
	printf ("Текущая языково-культурная среда:
		местная\n");
	print_curr_locale_data();
	plc = localeconv ();
	printf ("Некоторые элементы категорий
		LC_MONETARY и LC_NUMERIC\n");
	printf ("int_curr_symbol: %s\n",
		plc->int_curr_symbol);
	printf ("currency_symbol: %s\n",
		plc->currency_symbol);
	printf ("mon_decimal_point: %s\n",
		plc->mon_decimal_point);
	printf ("decimal_point: %s\n",
		plc->decimal_point);
	printf ("thousands_sep: %s\n",
		plc->thousands_sep);
	/* Сделаем "денежную" категорию украинской */
	if (setlocale (LC_MONETARY, "ru_UA") == NULL) {
		perror ("SETLOCALE");
		return (1);
	}
	printf ("Категория LC_MONETARY переустановлена
		для Украины\n");
	print_curr_locale_data();
	plc = localeconv ();
	printf ("Некоторые элементы категории
		LC_MONETARY\n");
	printf ("int_curr_symbol: %s\n",
		plc->int_curr_symbol);
	printf ("currency_symbol: %s\n",
		plc->currency_symbol);
	return 0;
}

Возможный результат работы этой программы показан в листинге 13.12.

Текущая языково-культурная среда: подразумеваемая
Цепочки символов, ассоциированные в текущей среде с
	LC_ALL: C
	LC_COLLATE: C
	LC_CTYPE: C
	LC_MESSAGES: C
	LC_MONETARY: C
	LC_NUMERIC: C
	LC_TIME: C
Текущая языково-культурная среда: местная
Цепочки символов, ассоциированные в текущей среде с
	LC_ALL: ru_RU.koi8r
	LC_COLLATE: ru_RU.koi8r
	LC_CTYPE: ru_RU.koi8r
	LC_MESSAGES: ru_RU.koi8r
	LC_MONETARY: ru_RU.koi8r
	LC_NUMERIC: ru_RU.koi8r
	LC_TIME: ru_RU.koi8r
Некоторые элементы категорий LC_MONETARY и LC_NUMERIC
	int_curr_symbol: RUR
	currency_symbol: РУБ
	mon_decimal_point: .
	decimal_point: ,
	thousands_sep: .
Категория LC_MONETARY переустановлена для Украины
Цепочки символов, ассоциированные в текущей среде с
	LC_ALL: LC_CTYPE=ru_RU.koi8r;LC_NUMERIC=ru_RU.koi8r;
	LC_TIME=ru_RU.koi8r;LC_COLLATE=ru_RU.koi8r;
	LC_MONETARY=ru_UA;LC_MESSAGES=ru_RU.koi8r;
	LC_PAPER=ru_RU.koi8r;LC_NAME=ru_RU.koi8r;
	LC_ADDRESS=ru_RU.koi8r;LC_TELEPHONE=ru_RU.koi8r;
	LC_MEASUREMENT=ru_RU.koi8r;
	LC_IDENTIFICATION=ru_RU.koi8r
	LC_COLLATE: ru_RU.koi8r
	LC_CTYPE: ru_RU.koi8r
	LC_MESSAGES: ru_RU.koi8r
	LC_MONETARY: ru_UA
	LC_NUMERIC: ru_RU.koi8r
	LC_TIME: ru_RU.koi8r
Некоторые элементы категории LC_MONETARY
	int_curr_symbol: UAH
	currency_symbol: ГР

Для преобразования денежных величин в цепочку символов в соответствии с настройками текущей языково-культурной среды можно воспользоваться функцией strfmon() (см. листинг 13.13), входящей в XSI-расширение стандарта POSIX-2001.

#include <monetary.h>
ssize_t strfmon (char *restrict s, 
    size_t maxsize,
    const char *restrict format, ...);

Результат преобразования (длиной не более maxsize байт) помещается в буфер, на который указывает аргумент s. Само преобразование выполняется под управлением аргумента format. В формат могут входить обычные символы, просто копируемые в выходную цепочку, а также спецификаторы преобразований – под их воздействием производится извлечение и вывод дополнительных аргументов.

Спецификатор преобразования в общем случае состоит из следующих компонентов:

  • символ %;
  • флаги;
  • ширина поля;
  • число символов в целой части;
  • число символов в дробной части;
  • символ, задающий преобразование.
  • Обязательными являются только первый и последний компоненты. Возможных преобразований два:

    i

    Преобразовать значение типа double в денежную величину международного формата.

    n

    Преобразовать значение типа double в денежную величину местного формата.

    В качестве результата функция strfmon() возвращает количество байт, помещенное в массив s.

    Может показаться, что в деле интернационализации/локализации категориям LC_MONETARY и LC_NUMERIC повезло больше других, поскольку для них определена многокомпонентная структура lconv, содержащая значения всех ключевых слов этих категорий, и функция localeconv(), позволяющая данную структуру получить. Однако на практике дело обстоит прямо противоположным образом. Для других категорий настройка приложений на местную языково-культурную среду сводится к вызову

    setlocale (LC_ALL, "");

    Всю остальную специфику среды учитывают функции isalpha(), tolower(), strcoll(), strftime() и т.д., которыми приложения могут пользоваться стандартными способами.

    В листинге 13.14 показан пример программы, использующей упомянутые функции. Видно, как меняется их поведение при модификации языково-культурной среды (см. листинг 13.15).

    #include <stdio.h>
    #include <ctype.h>
    #include <locale.h>
    #include <limits.h>
    #include <monetary.h>
    
    #define MY_VALUE 1234.567
    
    int main (void) {
    	char sbuf [LINE_MAX];
    	printf ("Текущая языково-культурная среда:
    		подразумеваемая\n");
    	printf ("isalpha ('Б'): %d\n", isalpha ('Б'));
    	printf ("tolower ('Б'): '%c'\n", tolower ('Б'));
    	printf ("Результат преобразования в денежную
    		величину числа 1234.567\n");
    	if (strfmon (sbuf, sizeof (sbuf), "Международное
    		обозначение: %i\n", MY_VALUE) == -1) {
    		perror ("STRFMON");
    		return (1);
    	}
    	fputs (sbuf, stdout);
    	(void) strfmon (sbuf, sizeof (sbuf), "Местное
    		обозначение: %n\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) setlocale (LC_ALL, "");
    	printf ("Текущая языково-культурная среда: местная\n");
    	printf ("isalpha ('Б'): %d\n", isalpha ('Б'));
    	printf ("tolower ('Б'): '%c'\n", tolower ('Б'));
    	printf ("Результат преобразования в денежную
    		величину числа 1234.567\n");
    	(void) strfmon (sbuf, sizeof (sbuf),
    		"Международное обозначение: %i\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) strfmon (sbuf, sizeof (sbuf), "Местное
    		обозначение: %n\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) setlocale (LC_MONETARY, "ru_UA");
    	printf ("Категория LC_MONETARY переустановлена
    		для Украины\n");
    	printf ("Результат преобразования в денежную
    		величину числа 1234.567\n");
    	(void) strfmon (sbuf, sizeof (sbuf),
    		"Международное обозначение: %i\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) strfmon (sbuf, sizeof (sbuf), "Местное
    		обозначение: %n\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	return 0;
    }

    Текущая языково-культурная среда: подразумеваемая

    isalpha ('Б'): 0
    tolower ('Б'): 'Б'
    Результат преобразования в денежную величину 
    числа 1234.567
    Международное обозначение: 1234.57
    Местное обозначение: 1234.57
    Текущая языково-культурная среда: местная
    isalpha ('Б'): 1024
    tolower ('Б'): 'б'
    Результат преобразования в денежную величину 
    числа 1234.567
    Международное обозначение: 1 234.57 RUR
    Местное обозначение: 1 234.57 руб
    Категория LC_MONETARY переустановлена для 
    Украины
    Результат преобразования в денежную величину 
    числа 1234.567
    Международное обозначение: 1 234.57 UAH
    Местное обозначение: 1 234.57 ГР

    Если требуется получить детальную информацию обо всех аспектах языково-культурной среды, можно воспользоваться функцией nl_langinfo() (см. листинг 13.16), отнесенной стандартом POSIX-2001 к расширению XSI.

    #include <langinfo.h>
    char *nl_langinfo (nl_item item);

    Аргументом функции nl_langinfo() могут служить именованные константы, определенные в заголовочном файле <langinfo.h>. В общем и целом они соответствуют описанным выше ключевым словам. В качестве результата nl_langinfo() возвращает указатель на цепочку символов, содержащую данные о выбранном элементе среды. Если в текущей среде этот элемент отсутствует, выдаются данные для POSIX-среды.

    Пример программы, использующей функцию nl_langinfo(), показан в листинге 13.17, возможные результаты ее работы – в листинге 13.18.

    #include <stdio.h>
    #include <locale.h>
    #include <langinfo.h>
    #include <regex.h>
    #include <limits.h>
    
    int main (void) {
    	regex_t cere; /* Скомпилированные расширенные */
    	/* регулярные выражения */
    	regex_t ceren;
    	int reerrcode; /* Код ошибки от regcomp или */
    	/* regexec */
    	char reerrbuf [LINE_MAX]; /* Буфер для строк с */
    	/* сообщениями об ошибках */
    	char response [LINE_MAX]; /* Буфер для ответа */
    
    	/* пользователя */
    	printf ("Текущая языково-культурная среда:
    		подразумеваемая\n");
    	printf ("Элемент YESEXPR категории LC_MESSAGES:
    		%s\n", nl_langinfo (YESEXPR));
    	printf ("Элемент MON_1 категории LC_TIME: %s\n",
    		nl_langinfo (MON_1));
    	(void) setlocale (LC_ALL, "");
    	printf ("Текущая языково-культурная среда:
    		местная\n");
    	printf ("Элемент YESEXPR категории LC_MESSAGES:
    		%s\n", nl_langinfo (YESEXPR));
    	printf ("Элемент MON_1 категории LC_TIME: %s\n",
    		nl_langinfo (MON_1));
    	/* Скомпилируем расширенное регулярное */
    	/* выражение для утвердительного ответа */
    	if ((reerrcode = regcomp (cere, nl_langinfo
    		(YESEXPR), REG_EXTENDED | REG_ICASE |
    		REG_NOSUB )) != 0) {
    		(void) regerror (reerrcode, cere, reerrbuf,
    		sizeof (reerrbuf));
    		fputs (reerrbuf, stderr);
    		fputc ('\n', stderr);
    		regfree (cere);
    		return (reerrcode);
    	}
    	/* То же для отрицательного ответа */
    	if ((reerrcode = regcomp (ceren, nl_langinfo
    		(NOEXPR), REG_EXTENDED | REG_ICASE |
    		REG_NOSUB )) != 0) {
    		(void) regerror (reerrcode, ceren, reerrbuf,
    		sizeof (reerrbuf));
    		fputs (reerrbuf, stderr);
    		fputc ('\n', stderr);
    		regfree (ceren);
    		return (reerrcode);
    	}
    	fputs ("Вы поддерживаете идею стандартизации
    		программных интерфейсов? ", stdout);
    	fgets (response, sizeof (response), stdin);
    	if (regexec (cere, response, 0, NULL, 0) == 0) {
    		fputs ("Ответ положительный\n", stdout);
    		} else if (regexec (ceren, response, 0, NULL, 0) == 0) {
    			fputs ("Ответ отрицательный\n", stdout);
    	} else {
    		fputs ("Ответ уклончивый\n", stdout);
    	}
    	regfree (cere);
    	regfree (ceren);
    	return 0;
    }
    Текущая языково-культурная среда: подразумеваемая
    Элемент YESEXPR категории LC_MESSAGES: ^[yY]
    Элемент MON_1 категории LC_TIME: January
    Текущая языково-культурная среда: местная
    Элемент YESEXPR категории LC_MESSAGES: ^[ДдYy].*
    Элемент MON_1 категории LC_TIME: Января
    Вы поддерживаете идею стандартизации программных интерфейсов? Да
    Ответ положительный

    К сожалению, обязательная часть стандарта POSIX-2001 содержит лишь минимум средств для настройки на целевую среду выдаваемых диагностических сообщений. По сути он сводится к функции strerror() (см. листинг 13.19), которая отображает номера (коды) ошибок в сообщения, зависящие от языково-культурной среды, точнее, от установки категории LC_MESSAGES.

    #include <string.h>
    char *strerror (int errnum);

    Обычно в качестве аргумента этой функции используют errno, но номера ошибок могут поступать и из произвольного источника.

    В листинге 13.20 показан пример программы, выдающей диагностические сообщения в разных средах и разными средствами – с помощью функций perror() и strerror().

    #include <stdio.h>
    #include <locale.h>
    #include <string.h>
    #include <errno.h>
    
    int main (void) {
    	fprintf (stderr, "Текущая языково-культурная
    		среда: подразумеваемая\n");
    	errno = 1;
    	perror ("PERROR, сообщение номер 1");
    	fprintf (stderr, "STRERROR, сообщение номер 1:
    		%s\n", strerror (1));
    	(void) setlocale (LC_ALL, "");
    	fprintf (stderr, "Текущая языково-культурная
    		среда: местная\n");
    	errno = 1;
    	perror ("PERROR, сообщение номер 1");
    	fprintf (stderr, "STRERROR, сообщение номер 1:
    		%s\n", strerror (1));
    	(void) setlocale (LC_MESSAGES, "ru_UA");
    	fprintf (stderr, "Категория LC_MESSAGES
    		переустановлена для Украины\n");
    	errno = 1;
    	perror ("PERROR, сообщение номер 1");
    	fprintf (stderr, "STRERROR, сообщение номер 1:
    		%s\n", strerror (1));
    return 0;
    }

    Средства для работы с каталогами сообщений

    Развитые средства для работы с каталогами сообщений ( диагностических, информационных), выдаваемых приложениями, вынесены в расширение XSI стандарта POSIX-2001. Идея этих средств состоит в том, чтобы в интернационализированных программах фигурировали не сами сообщения, а их идентификаторы в каталоге, который для каждой языково-культурной среды может быть своим.

    Стандарт не специфицирует формат каталогов сообщений, но предлагает служебную программу gencat для их генерации по исходному описанию:

    gencat каталог файл_сообщений ...

    Аргумент каталог задает маршрутное имя, под которым будет сохранен результат генерации.

    Исходное описание может состоять из нескольких файлов сообщений, строки которых, помимо прочих, содержат следующие директивы.

    $set идентификатор_набора_сообщений комментарий

    Задает идентификатор набора для дальнейших сообщений. Идентификатором служит целое число в диапазоне [1, NL_SETMAX]. В пределах одного исходного файла сообщений идентификаторы должны задаваться в порядке возрастания. Подразумеваемым является набор NL_SETD (см. <nl_types.h> ).

    идентификатор_сообщения текст_сообщения

    Идентификатором сообщения (в пределах набора) служит целое число в диапазоне [1, NL_MSGMAX]. В пределах одного набора идентификаторы сообщений задаются в порядке возрастания. Длина текста сообщения должна лежать в диапазоне [0, NL_TEXTMAX].

    Отметим, что генерируемые каталоги сообщений могут иметь двоичный формат и, следовательно, нуждаться в перегенерации при переносе на другие системы. Иными словами, и здесь (как и везде в стандарте POSIX) речь идет о мобильности на уровне исходных текстов.

    В прикладной программе работа с каталогами сообщений осуществляется посредством функций catopen(), catgets() и catclose() (см. листинг 13.21).

    #include <nl_types.h>
    nl_catd catopen (const char *name, int oflag);
    char *catgets (nl_catd catd, int set_id,
    	int msg_id, const char *s);
    int catclose (nl_catd catd);

    Функция catopen() открывает каталог сообщений и возвращает его дескриптор. Когда цепочка символов, на которую указывает аргумент name, содержит /, она трактуется как полное маршрутное имя. В противном случае (если пренебречь деталями) используется маршрут, зависящий от имени и реализации, а также от значения переменной окружения LANG (если аргумент oflag равен нулю) или LC_MESSAGES (если oflag равен NL_CAT_LOCALE ).

    К сожалению, рекомендации по расположению каталогов сообщений на момент написания данного текста еще не были выработаны. Стандарт POSIX-2001 ограничился замечанием о том, что авторам приложений следует соблюдать осторожность, дабы избежать конфликтов с другими приложениями и стандартными служебными программами.

    Дескриптор, выданный функцией catopen(), используется в вызове catgets() для чтения сообщения под номером msg_id из набора с номером set_id. Аргумент s указывает на подразумеваемую цепочку символов, которая будет возвращена catgets() в качестве результата, если запрошенное сообщение не удастся прочитать из каталога.

    Функция catclose() закрывает каталог сообщений.

    Страницы:

    Основные понятия и объекты

    Согласно стандарту POSIX-2001, языково-культурная среда – это часть пользовательского окружения, зависящая от языковых и культурных соглашений .

    Настройка на определенную языково-культурную среду представляет собой процесс формирования в системе совокупности данных, специфичных для поддержки конкретных естественных языков, местных настроек и кодировок символов. Иногда подобный процесс называют также локализацией, в противоположность интернационализации – процессу подготовки приложений, способных настраиваться на различные языково-культурные среды.

    Языково-культурная среда формируется из данных, принадлежащих нескольким именованным категориям. Каждая категория управляет определенными аспектами поведения компонентов системы. Имена и назначение категорий соответствуют следующим переменным окружения.

    LC_CTYPE

    Классификация символов, преобразование регистра (верхний/нижний) символов.

    LC_COLLATE

    Порядок алфавитного сравнения символов.

    LC_MONETARY

    Форматирование денежных величин.

    LC_NUMERIC

    Форматирование числовых (но не денежных) величин.

    LC_TIME

    Форматы даты и времени.

    LC_MESSAGES

    Форматы информационных и диагностических сообщений и интерактивных ответов.

    Категории подразделяются на более мелкие элементы, средством именования которых служат ключевые слова. Например, в категорию LC_CTYPE входят элементы с именами alpha (буквы), digits (цифры) и т.п.

    Как правило, значениями перечисленных переменных окружения являются абсолютные маршрутные имена файлов, содержащих соответствующие определения. Другой вариант (если значение не начинается с символа / ) – имя среды.

    Расширение XSI стандарта POSIX-2001 уточняет один из возможных форматов имени языково-культурной среды:

    язык[_территория][.кодировка][@модификатор]

    Примеры: ru_RU.koi8r, ru_UA.

    Модификатор позволяет выбрать конкретный вариант данных о среде в пределах отдельной категории (скажем, словарный порядок сравнения в категории LC_COLLATE ). Пример: LC_COLLATE=De_DE@dict.

    В каждой реализации определены одна или несколько языковокультурных сред. Поддержка POSIX-среды с именами-синонимами "POSIX" и "C" является обязательной. Кроме того, возможно создание новых сред, если в системе определен конфигурационный параметр _POSIX2_LOCALEDEF.

    Стандартом POSIX-2001 предусмотрены две "собирательные" переменные окружения, определяющие сразу все (или почти все) категории языково-культурной среды.

    LC_ALL

    Значение этой переменной окружения учитывается в первую очередь.

    LANG

    Значение этой переменной окружения учитывается в последнюю очередь, если не определены значения переменных вида LC_*.

    В любой реализации некоторая среда (POSIX или иная) должна быть определена как подразумеваемая, т. е. используемая тогда, когда перечисленные выше переменные окружения не определены или имеют пустые значения. Это можно сделать, например, путем присваивания LANG=POSIX или LANG=C, после чего подразумеваемой станет POSIX-среда.

    В структуре lconv, описанной во включаемом файле <locale.h>, содержатся поля, определяющие форматы денежных и числовых величин. Даже беглый взгляд на перечень этих полей позволяет составить представление о богатстве возможностей определения языково-культурной среды.

    char *currency_symbol; 
    /* Местное обозначение  денежной величины  */
    
    char *decimal_point; 
    /* Символ, отделяющий  целую часть         */
    /* числа от дробной                        */
    
    char frac_digits; 
    /* Количество цифр в дробной               */
    /* части местных денежных величин          */
    
    char *grouping; 
    /* Размеры групп цифр при                  */ 
    /* форматировании чисел                    */
    
    char *int_curr_symbol; 
    /* Международное обозначение денежной      */
    /* величины                                */
    
    char int_frac_digits; 
    /* Количество цифр в дробной части         */
    /* международных денежных величин          */
    
    char int_n_cs_precedes; 
    /* Признак того, что международное         */
    /* обозначение предшествует отрицательной  */
    /* денежной величине, а не следует за ней  */
    
    char int_n_sep_by_space; 
    /* Признак того, что международное         */
    /* обозначение отделяется от отрицательной */
    /* денежной величины пробелом              */
    
    char int_n_sign_posn; 
    /* Позиция знака минус в международных     */
    /* обозначенияхотрицательных денежных      */
    /* величин                                 */
    
    char int_p_cs_precedes; 
    /* Признак того, что международное          */
    /* обозначение предшествует неотрицательной */
    /* денежной величине, а не следует за ней   */
    /* */
    
    char int_p_sep_by_space; 
    /* Признак того, что международное          */
    /* обозначение отделяется от                */
    /* неотрицательной денежной величины        */
    /* пробелом                                 */
    
    char int_p_sign_posn; 
    /* Позиция знака плюс в международных       */
    /* обозначениях положительных денежных      */
    /* величин                                  */
    
    char *mon_decimal_point; 
    /* Символ, отделяющий целую часть денежной  */
    /* величины от дробной                      */
    
    char *mon_grouping; 
    /* Размеры групп цифр при форматировании    */
    /* денежных величин                         */
    
    char *mon_thousands_sep; 
    /* Символ, разделяющий группы цифр в        */
    /* денежных величинах                       */
    
    char *negative_sign; 
    /* Знак минус для отрицательных денежных    */
    /* величин                                  */
    
    char n_cs_precedes; 
    /* Признак того, что местное обозначение    */
    /* предшествует отрицательной денежной      */
    /* величине, а не следует за ней            */
    
    char n_sep_by_space; 
    /* Признак того, что местное обозначение    */
    /* отделяется от отрицательной денежной     */
    /* величины пробелом                        */
    
    char n_sign_posn; 
    /* Позиция знака минус в местных            */
    /* обозначениях отрицательных денежных      */
    /* величин                                  */
    
    char *positive_sign; 
    /* Знак плюс для неотрицательных            */
    /* денежных величин                         */
    
    char p_cs_precedes; 
    /* Признак того, что местное обозначение    */
    /* предшествует неотрицательной денежной    */
    /* величине, а не следует за ней            */
    
    char p_sep_by_space; 
    /* Признак того, что местное обозначение    */
    /* отделяется от неотрицательной денежной   */
    /* величины пробелом                        */
    
    char p_sign_posn; 
    /* Позиция знака плюс в местных             */
    /* обозначениях положительных денежных      */
    /* величин                                  */
    
    char *thousands_sep; 
    /* Символ, разделяющий группы цифр в        */
    /* числовых величинах                       */

    Стандарт POSIX-2001 регламентирует средства опроса и установки характеристик языково-культурной среды, а также опроса доступного набора таких сред.

    Общая логика использования средств интернационализации/локализации такова: пользователь, присваивая соответствующие значения описанным выше переменным окружения, идентифицирует свою языково-культурную среду; приложения могут опросить данные о целевой среде с помощью функции setlocale() и произвести настройку интернационализированных компонентов.

    Определение языково-культурной среды

    Определение языково-культурной среды помещается в файлы, к описанию формата которых мы и приступаем. Можно считать, что в подобных файлах, называемых далее файлами определения среды, содержится "исходный текст" определения, поскольку, чтобы возыметь действие, они должны быть обработаны утилитой localedef или эквивалентным средством.

    Файл определения среды должен содержать определение одной или нескольких категорий, которое может сводиться к директиве copy (см. далее).

    Определение категории состоит из заголовка, тела и хвостовика. Заголовок именует категорию; он должен начинаться с символов LC_ . Хвостовик строится из слова END и имени, употребленного в качестве заголовка.

    Первому заголовку могут предшествовать строки, переопределяющие символ комментария (по умолчанию – # ) и управляющий символ ( \ ).

    Тело категории состоит из одной или нескольких строк, содержащих идентификатор и, возможно, следующие за ним операнды. В качестве идентификатора может выступать как ключевое слово, именующее отдельный элемент языково-культурной среды, так и элемент алфавитного сравнения символов. Ключевые слова должны быть уникальными в пределах среды и не начинаться с префикса LC_.

    Операндами могут быть символы, элементы сравнения и цепочки символов. Цепочки символов заключаются в двойные кавычки, операнды разделяются точками с запятой и, возможно, пробелами.

    Символы и элементы сравнения представляются определенными ниже именами. Кроме того, символы могут обозначаться естественным образом или кодами; правда, при этом определение языково-культурной среды теряет в мобильности.

    Имена символов заключаются в угловые скобки (например, <A>, <z> и т.п.). Такая конструкция должна в точности совпадать с именем, определенным в файле отображения символов, который специфицирует опция -f утилиты localedef.

    Реализация должна поддерживать по крайней мере один набор символов и соответствующий файл отображения. Любой набор должен включать в себя все элементы из так называемого мобильного набора символов, определяемого стандартом POSIX-2001 (их имена перечислены в листинге 13.2).

    Файл отображения символов имеет довольно простую структуру. Если отвлечься от несущественных деталей, он должен состоять из директив вида

    <
    имя_символа> <код> <комментарий>

    Эти директивы заключаются между строками

    Эти директивы заключаются между строками

    и

    END CHARMAP

    Перед строкой CHARMAP может располагаться определение имени набора символов, которое задается директивой

    <
    code_set_name> имя

    Фрагмент возможного описания набора символов KOI8-R показан в листинге 13.1.

    <
    code_set_name> KOI8-R
    <comment_char> %
    <escape_char> /
    % version: 1.0
    % source: RFC1489 via Gabor Kiss <kissg@sztaki.hu>
    % and Andrey A. Chernov <ache@astral.msk.su>
    CHARMAP
    <U0000> /x00 NULL (NUL)
    <U0001> /x01 START OF HEADING (SOH)
    <U0002> /x02 START OF TEXT (STX)
    . . .
    <U0020> /x20 SPACE
    <U0021> /x21 EXCLAMATION MARK
    . . .
    <U0030> /x30 DIGIT ZERO
    <U0031> /x31 DIGIT ONE
    . . .
    <U0041> /x41 LATIN CAPITAL LETTER A
    <U0042> /x42 LATIN CAPITAL LETTER B
    . . .
    <U0061> /x61 LATIN SMALL LETTER A
    <U0062> /x62 LATIN SMALL LETTER B
    . . .
    <U044E> /xc0 CYRILLIC SMALL LETTER YU
    <U0430> /xc1 CYRILLIC SMALL LETTER A
    <U0431> /xc2 CYRILLIC SMALL LETTER BE
    . . .
    <U0429> /xfd CYRILLIC CAPITAL LETTER SHCHA
    <U0427> /xfe CYRILLIC CAPITAL LETTER CHE
    <U042A> /xff CYRILLIC CAPITAL LETTER HARD SIGN
    END CHARMAP

    Опишем теперь формат определения категорий языково-культурной среды.

    Категория LC_CTYPE определяет классификацию символов, преобразование регистра и другие атрибуты. К ней относятся следующие ключевые слова: upper (прописные буквы; для POSIX-среды – 26 латинских букв верхнего регистра), lower (строчные буквы), alpha (буквы), digit (цифры), alnum (буквы и цифры), space (пробельные символы; для POSIX-среды в их число, помимо пробела, входят перевод строки, т абуляция и т.п.), cntrl (управляющие символы), punct (знаки пунктуации), graph (печатные символы за исключением пробела), print (печатные символы), xdigit (шестнадцатеричные цифры), blank (для POSIX-среды – пробел и табуляция).

    С ключевым словом copy задается имя существующей среды, у которой наследуется определение категории. При наличии данной директивы другие ключевые слова недопустимы.

    Ключевое слово charclass позволяет задать имена специфичных для определяемой среды классов символов. Каждый такой класс далее должен быть описан в определении категории LC_CTYPE по образу и подобию перечисленных выше стандартных классов.

    Ключевое слово toupper открывает определение отображения малых букв в большие. Операнд этого слова представляет собой пару символов, заключенных в скобки и разделенных запятой. Аналогичным образом устроен элемент tolower.

    В листинге 13.2 показано определение категории LC_CTYPE для POSIX-среды.

    LC_CTYPE
    # POSIX-среда, категория LC_CTYPE
    # "alpha" по определению есть объединение элементов
    "upper" и "lower"
    # "alnum" по определению есть объединение элементов
    "alpha" и "digit"
    # "print" по определению есть объединение элементов
    "alnum", "punct" и <space>
    # "graph" по определению есть объединение элементов
    "alnum" и "punct"
    #
    upper <A>;<B>;<C>;<D>;<E>;<F>;<G>;<H>;<I>;<J>;<K>;\
    <L>;<M>;<N>;<O>;<P>;<Q>;<R>;<S>;<T>;<U>;<V>;<W>;\
    <X>;<Y>;<Z>
    #
    lower <a>;<b>;<c>;<d>;<e>;<f>;<g>;<h>;<i>;<j>;<k>;\
    <l>;<m>;<n>;<o>;<p>;<q>;<r>;<s>;<t>;<u>;<v>;<w>;\
    <x>;<y>;<z>
    #
    digit <zero>;<one>;<two>;<three>;<four>;<five>;\
    <six>;<seven>;<eight>;<nine>
    #
    space <tab>;<newline>;<vertical-tab>;<form-feed>;\
    <carriage-return>;<space>
    #
    cntrl <alert>;<backspace>;<tab>;<newline>;\
    <vertical-tab>;<form-feed>;<carriage-return>;\
    <NUL>;<SOH>;<STX>;<ETX>;<EOT>;<ENQ>;<ACK>;<SO>;\
    <SI>;<DLE>;<DC1>;<DC2>;<DC3>;<DC4>;<NAK>;<SYN>;\
    <ETB>;<CAN>;<EM>;<SUB>;<ESC>;<IS4>;<IS3>;<IS2>;\
    <IS1>;<DEL>
    #
    punct <exclamation-mark>;<quotation-mark>;\
    <number-sign>;<dollar-sign>;<percent-sign>;\
    <ampersand>;<apostrophe>;<left-parenthesis>;\
    <right-parenthesis>;<asterisk>;<plus-sign>;\
    <comma>;<hyphen>;<period>;<slash>;<colon>;\
    <semicolon>;<less-than-sign>;<equals-sign>;\
    <greater-than-sign>;<question-mark>;\
    <commercial-at>;<left-square-bracket>;\
    <backslash>;<right-square-bracket>;\
    <circumflex>;<underscore>;<grave-accent>;\
    <left-curly-bracket>;<vertical-line>;\
    <right-curly-bracket>;<tilde>
    #
    xdigit <zero>;<one>;<two>;<three>;<four>;<five>;\
    <six>;<seven>;<eight>;<nine>;<A>;<B>;<C>;<D>;\
    <E>;<F>;<a>;<b>;<c>;<d>;<e>;<f>
    #
    blank <space>;<tab>
    #
    toupper (<a>,<A>);(<b>,<B>);(<c>,<C>);(<d>,<D>);\
    (<e>,<E>);(<f>,<F>);(<g>,<G>);(<h>,<H>);\
    (<i>,<I>);(<j>,<J>);(<k>,<K>);(<l>,<L>);(<m>,<M>);\
    (<n>,<N>);(<o>,<O>);(<p>,<P>);(<q>,<Q>);(<r>,<R>);\
    (<s>,<S>);(<t>,<T>);(<u>,<U>);(<v>,<V>);(<w>,<W>);\
    (<x>,<X>);(<y>,<Y>);(<z>,<Z>)
    #
    tolower (<A>,<a>);(<B>,<b>);(<C>,<c>);(<D>,<d>);\
    (<E>,<e>);(<F>,<f>);(<G>,<g>);(<H>,<h>);\
    (<I>,<i>);(<J>,<j>);(<K>,<k>);(<L>,<l>);\
    (<M>,<m>);(<N>,<n>);(<O>,<o>);(<P>,<p>);\(
    <Q>,<q>);(<R>,<r>);(<S>,<s>);(<T>,<t>);\
    <U>,<u>);(<V>,<v>);(<W>,<w>);(<X>,<x>);(\
    <Y>,<y>);(<Z>,<z>)
    END LC_CTYPE

    Категория LC_COLLATE определяет порядок алфавитного сравнения символов для многочисленных служебных программ ( sort, uniq и т.д.), регулярных выражений, а также функций strcoll(), strxfrm() и других.

    В алфавитном сравнении могут участвовать односимвольные и многосимвольные элементы. Их порядок задается весами, которых у каждого элемента может быть несколько (не более COLL_WEIGHTS_MAX ). Первый вес называется основным. Если при сравнении элементов основные веса совпали, используются соответственные пары дополнительных – до тех пор, пока либо не будет обнаружено неравенство, либо не кончатся веса.

    Допускается формирование классов эквивалентности – присвоение одного основного веса нескольким элементам сравнения. Поддерживаются также отображения один-ко-многим (один символ отображается в последовательность элементов сравнения).

    Разумеется, сравнение цепочек символов начинается с разбиения на элементы сравнения.

    Правила алфавитного сравнения и директивы присвоения весов открываются ключевым словом order_start и завершаются директивой order_end. Подразумеваемым правилом является forward – сравнение от начала к концу цепочки. Возможен и противоположный порядок ( backward ).

    После строки order_start элементы сравнения перечисляются в порядке возрастания. После элемента может быть задана последовательность его весов. Веса задаются как относительные, в виде имен других элементов и определяются позицией этих элементов при перечислении. Специальный вес IGNORE означает, что данный элемент следует игнорировать при сравнении цепочек. Многоточие в качестве элемента сравнения обозначает диапазон от предыдущего до последующего элементов. Элемент UNDEFINED представляет все, что не было указано явно.

    В POSIX-среде алфавитный порядок совпадает с упорядоченностью символов в кодировке ASCII.

    Категория LC_MONETARY определяет формат денежных величин. Входящие в нее элементы именуются аналогично полям приведенной выше структуры lconv. Например, после ключевого слова currency_symbol задается цепочка символов, обозначающая местные денежные величины. Если ключевое слово данной категории отсутствует в файле определения среды либо его значением является пустая цепочка или -1, значит, соответствующий элемент в языково-культурной среде неспецифицирован.

    В POSIX-среде все элементы этой категории остаются неспецифицированными.

    В категории LC_NUMERIC, во многом аналогичной LC_MONETARY, определяются правила и обозначения, используемые применительно к числовым данным. Здесь также действует параллель со структурой lconv.

    В листинге 13.3 показано определение категории LC_NUMERIC для POSIX-среды. Собственно, специфицируется только десятичная точка.

    LC_NUMERIC
    # POSIX-среда, категория LC_NUMERIC
    #
    decimal_point "<period>"
    thousands_sep ""
    grouping -1
    #
    END LC_NUMERIC

    Элементы категории LC_TIME определяют интерпретацию спецификаторов преобразований и, тем самым, поведение служебной программы date, а также функций strftime(), strptime() и некоторых других. В число поддерживаемых ключевых слов входят abday (сокращенные названия дней недели, начиная с воскресенья; элемент соответствует спецификатору %a ), day (полные названия дней недели, %A ), abmon (сокращенные названия месяцев, %b ), mon (полные названия месяцев, %B ), d_t_fmt (принятое в данной языково-культурной среде представление даты и времени, %c ), d_fmt (принятое в данной среде представление даты, %x ), t_fmt (принятое в данной среде представление времени, %X ).

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

    направление:смещение:начальная_дата:
    конечная_дата:имя_эры:формат_эры

    Здесь направление – знак плюс или минус, смещение – ближайший к начальной дате номер года, начальная_дата – цепочка вида гггг/мм/дд (год, месяц и число начала эры), конечная_дата – год, месяц и число конца эры либо цепочки "-*" (конечной датой служит начало отсчета времени) или "+*" (конечной датой служит конец отсчета времени), формат_эры – цепочка, используемая при форматировании номеров года в эре.

    Отметим, что точка отсчета может быть как самой ранней, так и самой поздней в эре. Примерами служат две христианские эры – после и до Рождества Христова, соответственно.

    В листинге 13.4 показано определение категории LC_TIME для POSIX-среды.

    LC_TIME
    # POSIX-среда, категория LC_TIME
    #
    # Сокращенные названия дней недели (%a)
    abday "<S><u><n>";"<M><o><n>";"<T><u><e>";\
    "<W><e><d>";"<T><h><u>";"<F><r><i>";"<S><a><t>"
    #
    # Полные названия дней недели (%A)
    day "<S><u><n><d><a><y>";\
    "<M><o><n><d><a><y>";\
    "<T><u><e><s><d><a><y>";\
    "<W><e><d><n><e><s><d><a><y>";\
    "<T><h><u><r><s><d><a><y>";\
    "<F><r><i><d><a>\<y>";\
    "<S><a><t><u><r><d><a><y>"
    #
    # Сокращенные названия месяцев (%b)
    abmon "<J><a><n>";"<F><e><b>";"<M><a><r>";\
    "<A><p><r>";"<M><a><y>";"<J><u><n>";\
    "<J><u><l>";"<A><u><g>";"<S><e><p>";\
    "<O><c><t>";"<N><o><v>";"<D><e><c>"
    #
    # Полные названия месяцев (%B)
    mon "<J><a><n><u><a><r><y>"\
    "<F><e><b><r><u><a><r><y>";\
    "<M><a><r><c><h>";"<A><p><r><i><l>";\
    "<M><a><y>";"<J><u><n><e>";\
    "<J><u><l><y>";"<A><u><g><u><s><t>";\
    "<S><e><p><t><e><m><b><e><r>";\
    "<O><c><t><o><b><e><r>";\
    "<N><o><v><e><m><b><e><r>";\
    "<D><e><c><e><m><b><e><r>"
    #
    # Эквивалент AM/PM (%p) "AM";"PM"
    am_pm "<A><M>";"<P><M>"
    #
    # Принятое в POSIX-среде представление даты и
    времени (%c)
    # "%a %b %e %H:%M:%S %Y"
    d_t_fmt "<percent-sign><a><space><percent-sign>\
    <b><space><percent-sign><e><space>\
    <percent-sign><H><colon><percent-sign>\
    <M><colon><percent-sign><S><space>\
    <percent-sign><Y>"
    #
    # Принятое в POSIX-среде представление даты (%x)
    "%m/%d/%y"
    d_fmt "<percent-sign><m><slash><percent-sign><d>\
    <slash><percent-sign><y>"
    #
    # Принятое в POSIX-среде представление времени (%X)
    "%H:%M:%S"
    t_fmt "<percent-sign><H><colon><percent-sign><M>\
    <colon><percent-sign><S>"
    #
    # Принятое в POSIX-среде 12-часовое представление
    времени (%r) "%I:%M:%S %p"
    t_fmt_ampm "<percent-sign><I><colon><percent-sign\
    <M><colon><percent-sign><S><space>\
    <percent_sign><p>"
    #
    END LC_TIME

    Категория LC_MESSAGES играет весьма ограниченную роль, определяя положительные и отрицательные ответы. Соответственно, она содержит два элемента – yesexpr и noexpr, значениями которых служат расширенные регулярные выражения. Для POSIX-среды данная категория описывается так, как показано в листинге 13.5.

    LC_MESSAGES
    # POSIX-среда, категория LC_MESSAGES
    #
    yesexpr "<circumflex><left-square-bracket>\
    <y><Y><right-square-bracket>"
    #
    noexpr "<circumflex><left-square-bracket>\
    <n><N><right-square-bracket>"
    #
    END LC_MESSAGES

    Создание и опрос характеристик языково-культурной среды

    Для создания целевой языково-культурной среды служит утилита localedef:

    localedef [-c] [-f файл_отображения_символов] 
              [-i исходный_файл] 
              [-u имя_набора_символов]
              имя_целевой_среды

    Утилита localedef преобразует рассмотренное выше определение категорий языково-культурной среды из исходного формата в целевой, пригодный для использования функциями и служебными программами, поведение которых зависит от значений переменных окружения.

    Исходные данные для localedef поступают из файла с именем операнда опции -i или со стандартного ввода.

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

    Аргументом опции -f является маршрутное имя файла отображения символов. После опции -u можно задать имя нестандартного набора символов.

    Опция -c предписывает генерировать выходной файл даже при наличии предупреждений.

    Аргумент имя_целевой_среды идентифицирует созданную языково-культурную среду. Если это имя начинается с символа /, оно интерпретируется как маршрутное имя, под которым сохраняется сгенерированное определение целевой среды (формат результатов работы localedef стандартом не специфицируется); в противном случае интерпретация имени зависит от реализации, а созданная среда становится общедоступной.

    Для получения информации о языково-культурных средах предназначена служебная программа locale:

    locale [-a | -m]
    locale [-ck] имя ...

    Будучи вызванной без аргументов, утилита locale выдает на стандартный вывод сводную информацию о среде, которая состоит из значений соответствующих переменных окружения (описанных выше, а также дополнительных, определенных, быть может, целевой системой). Результат может выглядеть так, как показано в листинге 13.6. Кавычки вокруг значений всех переменных окружения, кроме LANG, означают, что они не были установлены явным образом, а определены по значению $LANG.

    LANG=ru_RU.koi8r
    LC_CTYPE="ru_RU.koi8r"
    LC_NUMERIC="ru_RU.koi8r"
    LC_TIME="ru_RU.koi8r"
    LC_COLLATE="ru_RU.koi8r"
    LC_MONETARY="ru_RU.koi8r"
    LC_MESSAGES="ru_RU.koi8r"
    LC_PAPER="ru_RU.koi8r"
    LC_NAME="ru_RU.koi8r"
    LC_ADDRESS="ru_RU.koi8r"
    LC_TELEPHONE="ru_RU.koi8r"
    LC_MEASUREMENT="ru_RU.koi8r"
    LC_IDENTIFICATION="ru_RU.koi8r"
    LC_ALL=

    При наличии аргументов выдается информация о некоторых (если задано ключевое слово) или обо всех (если указано имя категории) элементах категорий.

    Опциям служебной программы locale приписан следующий смысл.

    -a

    Выдать информацию обо всех общедоступных языково-культурных средах (включая POSIX-среду ).

    -m

    Выдать имена доступных файлов отображения символов.

    -c

    Выдавать имена выбранных (прямо или косвенно, посредством ключевых слов) категорий, что делает результаты более читабельными, когда они охватывают несколько категорий.

    -k

    Выдавать имена и значения заданных элементов категорий.

    Операнд имя может быть именем категории, элемента категории (ключевым словом) или зарезервированным словом charmap. В двух первых случаях выдается информация о поименованной категории или ее элементе, в последнем – имя файла отображения символов, который был использован при создании языково-культурной среды.

    Приведем еще два примера употребления утилиты locale. Данные о категории LC_TIME, выдаваемые по команде

    locale -ck LC_TIME

    могут выглядеть так, как показано в листинге 13.7.

    LC_TIME
    abday="Вск;Пнд;Втр;Срд;Чтв;Птн;Сбт"
    day="Воскресенье;Понедельник;Вторник;Среда;
    Четверг;Пятница;Суббота"
    abmon="Янв;Фев;Мар;Апр;Май;Июн;Июл;Авг;Сен;
    Окт;Ноя;Дек"
    mon="Января;Февраля;Марта;Апреля;Мая;Июня;
    Июля;Августа;Сентября;Октября;Ноября;Декабря"
    d_t_fmt="%a %d %b %Y %T"
    d_fmt="%d.%m.%Y"
    t_fmt="%T"
    . . .
    first_weekday=1
    first_workday=1
    cal_direction=1
    date_fmt="%a %b %e %H:%M:%S %Z %Y"
    time-codeset="KOI8-R"

    Второй пример демонстрирует фрагмент интернационализированного варианта shell-процедуры, анализирующей интерактивный ответ пользователя. Предполагается, что ответ является значением переменной response.

    if echo "$response" | grep -Eq 
       "$(locale yesexpr)"
    then
    echo "Ответ положительный"
    else
    echo "Ответ отрицательный"
    fi

    Программная установка и опрос характеристик языково-культурной среды

    Мы приступаем к рассмотрению функций, предназначенных для работы с языково-культурной средой.

    Функция setlocale() (см. листинг 13.9) служит для установки и/или опроса всей языково-культурной среды вызывающего процесса или отдельных категорий.

    #include <locale.h>
    char *setlocale (int category, 
        const char *locale);

    Аргумент category задает категорию ( LC_COLLATE, LC_CTYPE, LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME ), а если его значение равно LC_ALL, то и всю среду.

    Аргумент locale указывает на цепочку символов, содержащую требуемые установки для выбранной категории (категорий); в общем случае ее формат зависит от реализации. Имеется, однако, несколько предопределенных, стандартизованных цепочек.

    "POSIX" или "C"

    Специфицирует минимальную языково-культурную среду ( POSIX-среду ) для C-программ. При отсутствии явных обращений к функции setlocale() такая среда устанавливается по умолчанию при входе в main().

    ""

    Специфицирует зависящую от реализации местную языково-культурную среду, определяемую значениями переменных окружения LC_* и LANG. Интернационализированная программа должна выполнять вызов вида

    setlocale (LC_ALL, "");

    для настройки на местную среду выполнения.

    Если значением аргумента locale служит пустой указатель, функция setlocale() опрашивает текущее окружение и возвращает имя используемой языково-культурной среды или цепочку символов, ассоциированную с выбранной категорией. В противном случае в качестве результата возвращаются те же характеристики, но для новой среды. Результирующая цепочка (скопированная в отведенную прикладной программой область памяти) в сочетании с заданной категорией может быть использована в последующих вызовах функции setlocale() для восстановления соответствующей части языково-культурной среды.

    Разумеется, в случае неудачного завершения вызова setlocale() результатом служит пустой указатель.

    Получить детальную информацию о категориях LC_MONETARY и LC_NUMERIC текущей языково-культурной среды можно с помощью функции localeconv() (см. листинг 13.10).

    #include <locale.h>
    struct lconv *localeconv (void);

    Структура типа lconv была описана выше. Здесь мы отметим лишь два момента.

    Поля, имеющие тип char *, указывают на цепочки символов, каждая из которых (за исключением *decimal_point ) может иметь вид "" как признак того, что данное значение в текущей среде недоступно или имеет нулевую длину.

    Поля типа char содержат неотрицательные числа; CHAR_MAX указывает на то, что данное значение в текущей среде недоступно.

    Вызов localeconv() не может кончиться неудачей, однако следует учитывать, что возвращаемая структура может быть перезаписана последующими обращениями не только к localeconv(), но и к setlocale().

    Следующая программа (см. листинг 13.11) демонстрирует использование функций setlocale() и localeconv().

    #include <stdio.h>
    #include <locale.h>
    
    /* Функция опрашивает и выводит цепочки символов, */
    /* ассоциированные с текущей средой и ее */
    /* категориями */
    
    void print_curr_locale_data (void) {
    	/* Массив категорий для setlocale() */
    	int ctgrs [] = {LC_ALL, LC_COLLATE, LC_CTYPE,
    		LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME};
    	/* Массив имен категорий */
    	char *ctgrnms [] = {"LC_ALL", "LC_COLLATE",
    		"LC_CTYPE", "LC_MESSAGES", "LC_MONETARY",
    		"LC_NUMERIC", "LC_TIME"};
    	unsigned int i;
    	printf ("Цепочки символов, ассоциированные в
    		текущей среде с\n");
    	for (i = 0; i < sizeof (ctgrs) / sizeof (int); i++) {
    		printf (" %s: %s\n", ctgrnms [i], setlocale
    			(ctgrs [i], NULL));
    	}
    }
    
    int main (void) {
    	struct lconv *plc;
    	/* Убедимся, что подразумеваемой является */
    	/* POSIX-среда */
    	printf ("Текущая языково-культурная среда:
    		подразумеваемая\n");
    	print_curr_locale_data();
    		/* Установим местную среду */
    	(void) setlocale (LC_ALL, "");
    	/* Опросим и выдадим ее характеристики */
    	printf ("Текущая языково-культурная среда:
    		местная\n");
    	print_curr_locale_data();
    	plc = localeconv ();
    	printf ("Некоторые элементы категорий
    		LC_MONETARY и LC_NUMERIC\n");
    	printf ("int_curr_symbol: %s\n",
    		plc->int_curr_symbol);
    	printf ("currency_symbol: %s\n",
    		plc->currency_symbol);
    	printf ("mon_decimal_point: %s\n",
    		plc->mon_decimal_point);
    	printf ("decimal_point: %s\n",
    		plc->decimal_point);
    	printf ("thousands_sep: %s\n",
    		plc->thousands_sep);
    	/* Сделаем "денежную" категорию украинской */
    	if (setlocale (LC_MONETARY, "ru_UA") == NULL) {
    		perror ("SETLOCALE");
    		return (1);
    	}
    	printf ("Категория LC_MONETARY переустановлена
    		для Украины\n");
    	print_curr_locale_data();
    	plc = localeconv ();
    	printf ("Некоторые элементы категории
    		LC_MONETARY\n");
    	printf ("int_curr_symbol: %s\n",
    		plc->int_curr_symbol);
    	printf ("currency_symbol: %s\n",
    		plc->currency_symbol);
    	return 0;
    }

    Возможный результат работы этой программы показан в листинге 13.12.

    Текущая языково-культурная среда: подразумеваемая
    Цепочки символов, ассоциированные в текущей среде с
    	LC_ALL: C
    	LC_COLLATE: C
    	LC_CTYPE: C
    	LC_MESSAGES: C
    	LC_MONETARY: C
    	LC_NUMERIC: C
    	LC_TIME: C
    Текущая языково-культурная среда: местная
    Цепочки символов, ассоциированные в текущей среде с
    	LC_ALL: ru_RU.koi8r
    	LC_COLLATE: ru_RU.koi8r
    	LC_CTYPE: ru_RU.koi8r
    	LC_MESSAGES: ru_RU.koi8r
    	LC_MONETARY: ru_RU.koi8r
    	LC_NUMERIC: ru_RU.koi8r
    	LC_TIME: ru_RU.koi8r
    Некоторые элементы категорий LC_MONETARY и LC_NUMERIC
    	int_curr_symbol: RUR
    	currency_symbol: РУБ
    	mon_decimal_point: .
    	decimal_point: ,
    	thousands_sep: .
    Категория LC_MONETARY переустановлена для Украины
    Цепочки символов, ассоциированные в текущей среде с
    	LC_ALL: LC_CTYPE=ru_RU.koi8r;LC_NUMERIC=ru_RU.koi8r;
    	LC_TIME=ru_RU.koi8r;LC_COLLATE=ru_RU.koi8r;
    	LC_MONETARY=ru_UA;LC_MESSAGES=ru_RU.koi8r;
    	LC_PAPER=ru_RU.koi8r;LC_NAME=ru_RU.koi8r;
    	LC_ADDRESS=ru_RU.koi8r;LC_TELEPHONE=ru_RU.koi8r;
    	LC_MEASUREMENT=ru_RU.koi8r;
    	LC_IDENTIFICATION=ru_RU.koi8r
    	LC_COLLATE: ru_RU.koi8r
    	LC_CTYPE: ru_RU.koi8r
    	LC_MESSAGES: ru_RU.koi8r
    	LC_MONETARY: ru_UA
    	LC_NUMERIC: ru_RU.koi8r
    	LC_TIME: ru_RU.koi8r
    Некоторые элементы категории LC_MONETARY
    	int_curr_symbol: UAH
    	currency_symbol: ГР

    Для преобразования денежных величин в цепочку символов в соответствии с настройками текущей языково-культурной среды можно воспользоваться функцией strfmon() (см. листинг 13.13), входящей в XSI-расширение стандарта POSIX-2001.

    #include <monetary.h>
    ssize_t strfmon (char *restrict s, 
        size_t maxsize,
        const char *restrict format, ...);

    Результат преобразования (длиной не более maxsize байт) помещается в буфер, на который указывает аргумент s. Само преобразование выполняется под управлением аргумента format. В формат могут входить обычные символы, просто копируемые в выходную цепочку, а также спецификаторы преобразований – под их воздействием производится извлечение и вывод дополнительных аргументов.

    Спецификатор преобразования в общем случае состоит из следующих компонентов:

  • символ %;
  • флаги;
  • ширина поля;
  • число символов в целой части;
  • число символов в дробной части;
  • символ, задающий преобразование.
  • Обязательными являются только первый и последний компоненты. Возможных преобразований два:

    i

    Преобразовать значение типа double в денежную величину международного формата.

    n

    Преобразовать значение типа double в денежную величину местного формата.

    В качестве результата функция strfmon() возвращает количество байт, помещенное в массив s.

    Может показаться, что в деле интернационализации/локализации категориям LC_MONETARY и LC_NUMERIC повезло больше других, поскольку для них определена многокомпонентная структура lconv, содержащая значения всех ключевых слов этих категорий, и функция localeconv(), позволяющая данную структуру получить. Однако на практике дело обстоит прямо противоположным образом. Для других категорий настройка приложений на местную языково-культурную среду сводится к вызову

    setlocale (LC_ALL, "");

    Всю остальную специфику среды учитывают функции isalpha(), tolower(), strcoll(), strftime() и т.д., которыми приложения могут пользоваться стандартными способами.

    В листинге 13.14 показан пример программы, использующей упомянутые функции. Видно, как меняется их поведение при модификации языково-культурной среды (см. листинг 13.15).

    #include <stdio.h>
    #include <ctype.h>
    #include <locale.h>
    #include <limits.h>
    #include <monetary.h>
    
    #define MY_VALUE 1234.567
    
    int main (void) {
    	char sbuf [LINE_MAX];
    	printf ("Текущая языково-культурная среда:
    		подразумеваемая\n");
    	printf ("isalpha ('Б'): %d\n", isalpha ('Б'));
    	printf ("tolower ('Б'): '%c'\n", tolower ('Б'));
    	printf ("Результат преобразования в денежную
    		величину числа 1234.567\n");
    	if (strfmon (sbuf, sizeof (sbuf), "Международное
    		обозначение: %i\n", MY_VALUE) == -1) {
    		perror ("STRFMON");
    		return (1);
    	}
    	fputs (sbuf, stdout);
    	(void) strfmon (sbuf, sizeof (sbuf), "Местное
    		обозначение: %n\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) setlocale (LC_ALL, "");
    	printf ("Текущая языково-культурная среда: местная\n");
    	printf ("isalpha ('Б'): %d\n", isalpha ('Б'));
    	printf ("tolower ('Б'): '%c'\n", tolower ('Б'));
    	printf ("Результат преобразования в денежную
    		величину числа 1234.567\n");
    	(void) strfmon (sbuf, sizeof (sbuf),
    		"Международное обозначение: %i\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) strfmon (sbuf, sizeof (sbuf), "Местное
    		обозначение: %n\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) setlocale (LC_MONETARY, "ru_UA");
    	printf ("Категория LC_MONETARY переустановлена
    		для Украины\n");
    	printf ("Результат преобразования в денежную
    		величину числа 1234.567\n");
    	(void) strfmon (sbuf, sizeof (sbuf),
    		"Международное обозначение: %i\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	(void) strfmon (sbuf, sizeof (sbuf), "Местное
    		обозначение: %n\n", MY_VALUE);
    	fputs (sbuf, stdout);
    	return 0;
    }

    Текущая языково-культурная среда: подразумеваемая

    isalpha ('Б'): 0
    tolower ('Б'): 'Б'
    Результат преобразования в денежную величину 
    числа 1234.567
    Международное обозначение: 1234.57
    Местное обозначение: 1234.57
    Текущая языково-культурная среда: местная
    isalpha ('Б'): 1024
    tolower ('Б'): 'б'
    Результат преобразования в денежную величину 
    числа 1234.567
    Международное обозначение: 1 234.57 RUR
    Местное обозначение: 1 234.57 руб
    Категория LC_MONETARY переустановлена для 
    Украины
    Результат преобразования в денежную величину 
    числа 1234.567
    Международное обозначение: 1 234.57 UAH
    Местное обозначение: 1 234.57 ГР

    Если требуется получить детальную информацию обо всех аспектах языково-культурной среды, можно воспользоваться функцией nl_langinfo() (см. листинг 13.16), отнесенной стандартом POSIX-2001 к расширению XSI.

    #include <langinfo.h>
    char *nl_langinfo (nl_item item);

    Аргументом функции nl_langinfo() могут служить именованные константы, определенные в заголовочном файле <langinfo.h>. В общем и целом они соответствуют описанным выше ключевым словам. В качестве результата nl_langinfo() возвращает указатель на цепочку символов, содержащую данные о выбранном элементе среды. Если в текущей среде этот элемент отсутствует, выдаются данные для POSIX-среды.

    Пример программы, использующей функцию nl_langinfo(), показан в листинге 13.17, возможные результаты ее работы – в листинге 13.18.

    #include <stdio.h>
    #include <locale.h>
    #include <langinfo.h>
    #include <regex.h>
    #include <limits.h>
    
    int main (void) {
    	regex_t cere; /* Скомпилированные расширенные */
    	/* регулярные выражения */
    	regex_t ceren;
    	int reerrcode; /* Код ошибки от regcomp или */
    	/* regexec */
    	char reerrbuf [LINE_MAX]; /* Буфер для строк с */
    	/* сообщениями об ошибках */
    	char response [LINE_MAX]; /* Буфер для ответа */
    
    	/* пользователя */
    	printf ("Текущая языково-культурная среда:
    		подразумеваемая\n");
    	printf ("Элемент YESEXPR категории LC_MESSAGES:
    		%s\n", nl_langinfo (YESEXPR));
    	printf ("Элемент MON_1 категории LC_TIME: %s\n",
    		nl_langinfo (MON_1));
    	(void) setlocale (LC_ALL, "");
    	printf ("Текущая языково-культурная среда:
    		местная\n");
    	printf ("Элемент YESEXPR категории LC_MESSAGES:
    		%s\n", nl_langinfo (YESEXPR));
    	printf ("Элемент MON_1 категории LC_TIME: %s\n",
    		nl_langinfo (MON_1));
    	/* Скомпилируем расширенное регулярное */
    	/* выражение для утвердительного ответа */
    	if ((reerrcode = regcomp (cere, nl_langinfo
    		(YESEXPR), REG_EXTENDED | REG_ICASE |
    		REG_NOSUB )) != 0) {
    		(void) regerror (reerrcode, cere, reerrbuf,
    		sizeof (reerrbuf));
    		fputs (reerrbuf, stderr);
    		fputc ('\n', stderr);
    		regfree (cere);
    		return (reerrcode);
    	}
    	/* То же для отрицательного ответа */
    	if ((reerrcode = regcomp (ceren, nl_langinfo
    		(NOEXPR), REG_EXTENDED | REG_ICASE |
    		REG_NOSUB )) != 0) {
    		(void) regerror (reerrcode, ceren, reerrbuf,
    		sizeof (reerrbuf));
    		fputs (reerrbuf, stderr);
    		fputc ('\n', stderr);
    		regfree (ceren);
    		return (reerrcode);
    	}
    	fputs ("Вы поддерживаете идею стандартизации
    		программных интерфейсов? ", stdout);
    	fgets (response, sizeof (response), stdin);
    	if (regexec (cere, response, 0, NULL, 0) == 0) {
    		fputs ("Ответ положительный\n", stdout);
    		} else if (regexec (ceren, response, 0, NULL, 0) == 0) {
    			fputs ("Ответ отрицательный\n", stdout);
    	} else {
    		fputs ("Ответ уклончивый\n", stdout);
    	}
    	regfree (cere);
    	regfree (ceren);
    	return 0;
    }
    Текущая языково-культурная среда: подразумеваемая
    Элемент YESEXPR категории LC_MESSAGES: ^[yY]
    Элемент MON_1 категории LC_TIME: January
    Текущая языково-культурная среда: местная
    Элемент YESEXPR категории LC_MESSAGES: ^[ДдYy].*
    Элемент MON_1 категории LC_TIME: Января
    Вы поддерживаете идею стандартизации программных интерфейсов? Да
    Ответ положительный

    К сожалению, обязательная часть стандарта POSIX-2001 содержит лишь минимум средств для настройки на целевую среду выдаваемых диагностических сообщений. По сути он сводится к функции strerror() (см. листинг 13.19), которая отображает номера (коды) ошибок в сообщения, зависящие от языково-культурной среды, точнее, от установки категории LC_MESSAGES.

    #include <string.h>
    char *strerror (int errnum);

    Обычно в качестве аргумента этой функции используют errno, но номера ошибок могут поступать и из произвольного источника.

    В листинге 13.20 показан пример программы, выдающей диагностические сообщения в разных средах и разными средствами – с помощью функций perror() и strerror().

    #include <stdio.h>
    #include <locale.h>
    #include <string.h>
    #include <errno.h>
    
    int main (void) {
    	fprintf (stderr, "Текущая языково-культурная
    		среда: подразумеваемая\n");
    	errno = 1;
    	perror ("PERROR, сообщение номер 1");
    	fprintf (stderr, "STRERROR, сообщение номер 1:
    		%s\n", strerror (1));
    	(void) setlocale (LC_ALL, "");
    	fprintf (stderr, "Текущая языково-культурная
    		среда: местная\n");
    	errno = 1;
    	perror ("PERROR, сообщение номер 1");
    	fprintf (stderr, "STRERROR, сообщение номер 1:
    		%s\n", strerror (1));
    	(void) setlocale (LC_MESSAGES, "ru_UA");
    	fprintf (stderr, "Категория LC_MESSAGES
    		переустановлена для Украины\n");
    	errno = 1;
    	perror ("PERROR, сообщение номер 1");
    	fprintf (stderr, "STRERROR, сообщение номер 1:
    		%s\n", strerror (1));
    return 0;
    }

    Средства для работы с каталогами сообщений

    Развитые средства для работы с каталогами сообщений ( диагностических, информационных), выдаваемых приложениями, вынесены в расширение XSI стандарта POSIX-2001. Идея этих средств состоит в том, чтобы в интернационализированных программах фигурировали не сами сообщения, а их идентификаторы в каталоге, который для каждой языково-культурной среды может быть своим.

    Стандарт не специфицирует формат каталогов сообщений, но предлагает служебную программу gencat для их генерации по исходному описанию:

    gencat каталог файл_сообщений ...

    Аргумент каталог задает маршрутное имя, под которым будет сохранен результат генерации.

    Исходное описание может состоять из нескольких файлов сообщений, строки которых, помимо прочих, содержат следующие директивы.

    $set идентификатор_набора_сообщений комментарий

    Задает идентификатор набора для дальнейших сообщений. Идентификатором служит целое число в диапазоне [1, NL_SETMAX]. В пределах одного исходного файла сообщений идентификаторы должны задаваться в порядке возрастания. Подразумеваемым является набор NL_SETD (см. <nl_types.h> ).

    идентификатор_сообщения текст_сообщения

    Идентификатором сообщения (в пределах набора) служит целое число в диапазоне [1, NL_MSGMAX]. В пределах одного набора идентификаторы сообщений задаются в порядке возрастания. Длина текста сообщения должна лежать в диапазоне [0, NL_TEXTMAX].

    Отметим, что генерируемые каталоги сообщений могут иметь двоичный формат и, следовательно, нуждаться в перегенерации при переносе на другие системы. Иными словами, и здесь (как и везде в стандарте POSIX) речь идет о мобильности на уровне исходных текстов.

    В прикладной программе работа с каталогами сообщений осуществляется посредством функций catopen(), catgets() и catclose() (см. листинг 13.21).

    #include <nl_types.h>
    nl_catd catopen (const char *name, int oflag);
    char *catgets (nl_catd catd, int set_id,
    	int msg_id, const char *s);
    int catclose (nl_catd catd);

    Функция catopen() открывает каталог сообщений и возвращает его дескриптор. Когда цепочка символов, на которую указывает аргумент name, содержит /, она трактуется как полное маршрутное имя. В противном случае (если пренебречь деталями) используется маршрут, зависящий от имени и реализации, а также от значения переменной окружения LANG (если аргумент oflag равен нулю) или LC_MESSAGES (если oflag равен NL_CAT_LOCALE ).

    К сожалению, рекомендации по расположению каталогов сообщений на момент написания данного текста еще не были выработаны. Стандарт POSIX-2001 ограничился замечанием о том, что авторам приложений следует соблюдать осторожность, дабы избежать конфликтов с другими приложениями и стандартными служебными программами.

    Дескриптор, выданный функцией catopen(), используется в вызове catgets() для чтения сообщения под номером msg_id из набора с номером set_id. Аргумент s указывает на подразумеваемую цепочку символов, которая будет возвращена catgets() в качестве результата, если запрошенное сообщение не удастся прочитать из каталога.

    Функция catclose() закрывает каталог сообщений.

    Вернуться к учебному плану