Иллюстрированный самоучитель по практике программирования

Интернационализация

Если вы живете в Соединенных Штатах, то вы, может быть, забыли, что английский – не единственный язык на свете, ASCII – не единственный набор символов, $ – не единственный символ валюты, что даты могут записываться с указанием сначала дня, а потом уже месяца, что время может записываться в формате с 24-мя часами и т. п. Так вот, еще один аспект переносимости в общем виде связан с созданием программ, переносимых между разными языками и культурными границами. Это на самом деле весьма обширная тема для разговора, и мы будем вынуждены ограничиться освещением лишь нескольких основных концепций.

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

Не рассчитывайте на ASCII.
В большинстве стран мира наборы символов богаче, чем ASCII. Стандартная функция проверки символов из ctype.h, в общем, успешно справляется с этими различиями:

if (isalpha(c))…

Такое выражение не зависит от конкретной кодировки символов, а главное – если программу скомпилировать в локальной среде, то она будет работать корректно и в тех случаях, когда букв больше или меньше, чем от а до r. Правда, имя isalpha ("это буква?") говорит само за себя, а ведь существуют языки, в которых алфавита нет вообще.

В большинстве европейских стран кодировка ASCII, определяющая только значения до 0x7F (7 битов), расширяется дополнительными символами, которые представляют собой буквы национальных языков.

Кодировка Latin-1, широко распространенная в Западной Европе, является расширением ASCII, определяющим значения байтов от 80 до FF небуквенных символов и акцентированных букв – так, значение Е7 представляет букву д. Английское слово boy представляется в ASCII ли Latin-1 тремя байтами с шестнадцатеричными значениями 62 6F 79, французское слово lа rсоn представляется в Latin-1 байтами 67 61 72 Е7 6Е. В других языках определяются, соответственно, другие символы, но они не могут уложиться в 128 значений, не используемых в ASCII, к что существует множество конфликтующих стандартов для символов, привязанных к байтам от 80 до FF.

Некоторым языкам вообще не хватает 8 битов: в большинстве азиат-их языков существуют тысячи символов. В кодировках, используемых в Китае, Японии и Корее, на символ отводится 16 битов. В результате возникает глобальная проблема переносимости: как прочитать документ на некотором языке на компьютере, настроенном на другой язык. Даже если все символы передадутся без ошибок, для прочтения на американском компьютере документа на китайском языке должны как минимум стоять специальные шрифты и соответствующее программное обеспечение. Если же мы захотим на одной машине использовать английский, китайский и русский языки, проблем у нас возникнет море.

Набор символов Unicode – попытка улучшить описанную ситуацию, предоставив единую кодировку для всех языков мира. Unicode совместима с 16-битовым подмножеством стандарта ISO 10646; в ней используется 16 битов на символ. Значения от OOFF и ниже относятся к Latin-1,то есть слово gargon будет представлено 16-битовыми значениями 0067 61 0072 ООЕ7 006F 006Е. Кириллица занимает значения от 0401 до 04FF,а идеографическим языкам отведен большой блок, начинающийся с IOОО. Все известные и некоторые почти неизвестные языки мира представлены в Unicode, так что именно этой кодировкой и стоит пользовать для передачи документов между странами или для хранения текста, написанного на разных языках. Unicode стала весьма популярна в Интернете, и некоторые языки программирования даже поддерживают ее как стандартный формат: например, Java использует Unicode как родной набор символов для строк. Операционные системы Plan 9 и Inferno используют Unicode более широко – даже для имен файлов и пользователей, Microsoft Windows поддерживает набор символов Unicode, но не считает его стандартом; большинство приложений Windows до сих пор лучше работает с ASCII, хотя соотношение стремительно меняется в пользу Unicode.

Надо сказать, что и у Unicode есть недостатки: символы в ней уже не вмещаются в один байт, поэтому текст в Unicode страдает от проблемы порядка байтов. Для преодоления этой напасти документы в Unicod перед передачей между программами или по сети обычно преобразуются в кодировку потока байтов, называемую UTF-8. В ней каждый 16-битовый символ кодируется для передачи как последовательность из 1, 2 или 3 байтов. Набор символов ASCII использует значения от 00 до 7F, все они умещаются в один байт при использовании UTF-8. Таким образом, получается, что UTF-8 односторонне совместима с ASCII. Значения между 80 и 7FF представляются двумя байтами, а значения от 800 и выше – тремя.1 В UTF-8 слово gargon представляется байтами 67 61 72 С3 А7 6F 6Е; значение Unicode E7 – символ g – представляется в UTF-8 двумя байтами – С3 А7.

Совместимость UTF-8 с ASCII весьма полезна, поскольку благодаря ей программы, рассматривающие текст как непрерывный поток байтов, могут работать с текстом Unicode на любом языке. Мы опробовали программу markov из третьей главы с текстом в UTF-8 на русском, греческом, японском и китайском языках, и она работала без каких-либо проблем. Для европейских языков, слова в которых разделяются ASCII-символами пробелов, табуляции или перевода строки, программа выдавала вполне сносный текст. При использовании других языков для того, чтобы получить что-то приемлемое на выходе, пришлось бы изменять правила разбиения текста на слова.

С и C++ поддерживают "широкие символы" (wide characters), которые представляются 16-битовыми или еще большими целыми. Существуют и соответствующие функции, которые могут быть использованы для обработки символов в Unicode или в другом расширенном наборе символов. Строковые константы из широких символов записываются как L"..,". Однако и здесь возникает большая проблема с переносимостью: программа с константами из широких символов может быть воспроизведена только на дисплее, использующем тот же набор символов. Поскольку символы должны быть конвертированы в поток байтов вроде UTF-8 для передачи между машинами, язык С предоставляет функции для преобразования широких символов в байты и обратно. Однако какое преобразование использовать? Интерпретация набора символов и описания кодировки потока байтов таятся в недрах библиотек, и вытащить их оттуда достаточно сложно; ситуация складывается не в нашу пользу. Может статься, в отдаленном светлом будущем все наконец придут к согласию об использовании единого набора символов, но пока что от фоблемы порядка байтов никуда нам не деться.

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

Как же быть с сообщениями об ошибках? По крайней мере, в них не должно использоваться жаргона или сленга; лучше всего писать на самом простом языке. Полезно еще собрать тексты всех сообщений в каком-то одном месте программы – тогда можно будет быстро перевести их все.

Существует множество местных культурных особенностей, например формат дат mm/dd/yy используется только в Северной Америке. Если существует вероятность того, что ваша программа будет использоваться в другой стране, от таких особенностей надо по возможности избавиться. Иконки в графическом интерфейсе очень часто зависят от традиций; если понятия, на которых базируется зрительный образ, пользователю незнакомы, такая иконка его только дезориентирует.

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.