Этот пост — продолжение предыдущего “Добавляем internationalization в проект“.
Итак, моя вавилонская башня завершена. Все строители успешно разговаривают на UTF-8
. Поддержка i18n была сделана для следующих платформ:
- Windows
- Linux
- Solaris
- AIX
Хочется немного раскрыть особенности текстовой конвертации для каждой из платформ. Так как в результате пришлось написать что-то вроде мультиплатформенной компоненты, которая имела одинаковый интерфейс на любой из платформ. Пользоваться пришлось тем, что можно спокойно засунуть в поставку продукта, не заботясь о “проблемах” лицензирования. Т.е. абсолютно свободные библиотеки.
Итак, по-порядку:
- Windows
- получаем идентификатор входной строки и идентификатор выходной строки
- переводим входную строку с помошью функции MultiByteToWideChar() к UTF-16 строке wchar_t *
- затем обратно к нужной кодировке используя WideCharToMultiByte().
- Linux, Solaris, AIX
- используя текстовое название входной и выходной кодировок получаем дескриптор конвертора с помощью iconv_open()
- используя дескриптор конвертора переводим входную строку в выходную с помощью iconv()
- Linux
- Solaris
- AIX
Кодировка задается числом(!), а не текстовой строкой. Поэтому необходимо построить мэппинг из текстового представления кодировки (например “UTF-8“, “big5“, “IBM855“) в идентификатор кодовой страницы (UINT). Стандартными средствами (WINAPI) этого сделать невозможно. Поэтому пришлось решать эту задачу “в лоб”. Благо MSDN представляет полный список всех возможных кодировок с описаниями и числовыми значениями. Пару взмахов ViM‘ом и список можно превратить в однозначный мэппинг:
“текстовое имя кодировки” = номер кодовой страницы;
Для быстроты/удобства получения числового значения из текстового можно использовать HashSet с ключом — “текстовое имя кодировки” и значением — номер кодовой страницы. Для мэппинга из номера в текст проще всего использовать обычный массив. Благо его размер нам изначально известен.
Выяснение кодировки текущей локали оказалось крайне простым занятием: GetACP() возвращает UINT идентификатор кодировки текущей локали.
Схема конвертирования проста:
Для этих платформ ядром конвертации является библиотека ICONV. Не смотря на ее простоту, существует один интересный момент (!) с которым придется столкнуться при ее использовании. Дело в том, что в природе существует как минимум 2 версии этой библиотеки. Более того, они как правило, неплохо уживаются на одной системе. Это системная реализация и реализация GNU. Лицензия GNU’шной реализации библиотеки ICONV не позволяет использовать ее в проектах с закрытым кодом, ну или как-то так
. Так что надо обязательным образом использовать системный вариант, так как он, являясь частью системной библиотеки, вообще не нуждается в том, чтобы его включали в поставку. Другими словами, системная реализация ICONV гарантированно всегда присутствует на любой из перечисленных платформ (что необязательно для GNU реализации), а потому включать ее еще в дистрибутив своего продукта смысла нет, а раз она не включается в дистрибутив продукта, то и нет никаких проблем с лицензией. Фув. В этом и есть основное различие использования библиотеки ICONV на перечисленных платформах.
Выяснение текущей локали оказалось задачей решаемой. Ранее я писал, что функция nl_langinfo() не работает, как описано в man‘е. Оказалось, что достаточно добавить вызов: setlocale(LC_ALL, “”); перед вызовом nl_langinfo() и все начинает отлично работать. Это известный подход, видимо в прошлый раз я просто не догадался его опробовать.
Схема конвертации:
Особенности:
Библиотека ICONV входит в libc.so.
Библиотека ICONV входит в libc.so.
Чтобы заставить работать функции стандартной библиотеки необходимо объявить макрос:
#define LIBICONV_PLUG
перед подключением системных хедеров. Иначе, по умолчанию, будет использоваться реализация GNU.
Библиотека ICONV входит в iconv.so, которая является частью системы.




Discussion