RDM/2 The Russian Electronic Developer Magazine  
RDM/2 Русский электронный журнал разработчика  
ДомойОт редактораПишите намОбратная связьRU/2

Говорите по-русски

Написать этот текст меня подвигли несколько причин: во-первых, крайне мало правильно русифицированных программ; во-вторых, если они и русифицированы, то сделано это каждый раз по-разному; в-третьих, я попытался сам написать несколько программ, которые должны были работать с разными языками и разными кодировками.
Замечание 1. Меня очень сильно удивил тот факт, что никто не кричит на всех углах о русификации IBM AIX, и тем не менее оказалось, что она вполне правильно "говорит" по-русски (впрочем, как на доброй сотне других и всё это в одном дистрибутиве).
Оказывается, уже давно существуют стандарты Posix на поддержку языков. OS/2 не исключение, поддержка языков (после некоторого момента :-) является одной из сильных сторон этой ОС. В руководстве пользователя по VisualAge C++ (в моем случае это v3.0) есть целая глава "Making Your Program International" (делайте Ваши программы интернациональными).

Базовым понятием в языковой поддержке является "locale" ("локаль"), которая включает в себя поддержку кодовых страниц и наборов символов; сортировки и сравнения строк, классификации символов (знаки пунктуации, цифры и т.п.); поддержку перевода на нижний/верхний регистр; формат даты и времени; форматы чисел. Соответственно, в своей программе Вы можете использовать как отдельные элементы (например, только форматы представления денег) локализации, так и все вместе. Что самое удобное, так это то, что эта поддержка абсолютно прозрачна и не требует специальной модификации программ. Такие функции, как strcmp, tolower, ispunct при смене языка работают как должно (практически все функции работы со строками и символами правильно обрабатывают русские символы).

Как задать "locale"? Необходимо в начале программы вызвать функцию setlocale (LC_ALL,""); -- такой вызов установит locale в соответствии с установленной в системе (переменная LANG) для всех аспектов языка; setlocale(LC_ALL,"RU_RU"); - такой вызов установит locale - "русский".

Первый параметр задаёт "область действия" locale (например LC_TIME - только для представления времени, LC_COLLATE - только для сравнения строк). Второй параметр - имя "locale" (Вы уже догадались, зачем на загрузочном диске OS/2 находятся каталоги LANGUAGE\LOCALE, IBMI18N\LOCALE). И всё... Согласитесь, что это совсем немного, если теперь в зависимости от установленной "locale" можно воспользоваться данными соответствующими данному языку из русурсов?

Замечание 2. При необходимости достаточно просто можно описать и создать новую "locale", скомпилировать её, положить в один из каталогов, описанных в переменной LOCPATH, и пользоваться.
  #include 
  #include 
  #include 
  ...
      char *pclocale;
      char pcLocale[32];
                         // get LANG environment variable
  if ( (pclocale=getenv("LANG")) == NULL )
      pclocale = "RU_RU";// Default is Russian !
  setlocale ( LC_ALL, pclocale );
  strcpy ( pcLocale, nl_langinfo(CODESET) );
                         // setup PC codepage for
  ...                    // name translation
Данный пример получает значение переменной окружения LANG, если она не задана, то выбирается русская, затем устанавливается "locale" программы, и, последнее, программа получает имя кодовой страницы.

Кроме вышеописанного, данный интерфейс позволяет производить и перекодировку символов из одной кодовой страницы в другую. Это избавляет от необходимости иметь массу файлов с таблицами перекодировки для каждой из программ, нуждающихся в этом. Ниже приведен пример, который производит перекодировку из 866 в 1251 и обратно.

  char      pcLocale [32] = "IBM-866";
  char      psionLocale [32] = "IBM-1251";
  iconv_t cd_2psion;
  iconv_t cd_2pc;

  ...   // initialization, open translation tables
  cd_2psion = iconv_open ( psionLocale, pcLocale );
  cd_2pc    = iconv_open ( pcLocale, psionLocale );
  ...

  char *toPsiName ( char fname[] )
  {
          unsigned int ilen, olen;
          char *ibuf, *obuf;
          CHAR buf [_MAX_PS_PATH];

  olen = ilen = strlen(fname)+1;
  obuf = (char *)&buf;
  ibuf = (char *)fname;
  strcpy ( buf, fname );
  if ( cd_2psion != (iconv_t)(-1) )
      iconv ( cd_2psion, &ibuf, &ilen, &obuf, &olen );
  strcpy ( fname, buf );
  return ( fname );
  }

  char *toOsName ( char fname[] )
  {
          unsigned int ilen, olen;
          char *ibuf, *obuf;
          char buf [_MAX_PS_PATH];

  olen = ilen = strlen(fname)+1;
  obuf = (char *)&buf;
  ibuf = (char *)fname;
  strcpy ( buf, fname );
  if ( cd_2pc != (iconv_t)(-1) )
      iconv ( cd_2pc, &ibuf, &ilen, &obuf, &olen );
  strcpy ( fname, buf );
  return ( fname );
  }
К сожалению, несмотря на декларированную в документации VisualAge целостность языковой поддержки, не обошлось без ложки дёгтя. Все необходимые DLL-ли и таблицы перекодировки находятся только в каталогах \IBMCPP\LOCALE\ICONV и \IBMCPP\LOCALE\UCONVTAB, и, соответственно, не включены в дистрибутив OS/2. Если нужно, чтобы Ваши программы производили перекодировку, то необходимо позаботится, чтобы эти каталоги попали в один из каталогов, указанных в переменной LOCPATH. Причём действительно необходимым является весь каталог ICONV, а из каталога UCONVTAB Вас будут интересовать только используемые Вами таблицы перекодировки.
Замечание 3. Пользователи Warp 3, 4 должны еще скопировать подкаталоги RU_RU, EN_US (или другие для нужных языков).
Андрей А. Породько
P.S. Используя данную технику была создана небольшая программа-перекодировщик для сервисов TCP/IP. Желающие могут взять ее здесь и покопаться.

---
Интересные ссылки:

---

---
Комментариев к странице: 0 | Добавить комментарий
---
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов
(C) Russian Underground/2