Jak psát lokalizované programy
                       -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

          If the  Tao is great,  then  the  operating  system is great. If
      the  operating  system is great,  then the compiler is great. If the
      compiler is great, then the application is great. If the application
      is great,  then  the  user is pleased  and  there is  harmony in the
      world.
          The Tao gave birth to machine  language.  Machine  language gave
      birth to the assembler.
          The  assembler  gave  birth to the  compiler.  Now there are ten
      thousand languages.
          Each language has its purpose,  however  humble.  Each  language
      expresses the Yin and Yang of software. Each  language has its place
      within the Tao.
          But do not program in COBOL if you can avoid it.
                  -- Geoffrey James, "The Tao of Programming"

                Michael Mráka a Vladimír Michl, 9. června 1998

          Tento článek by měl přinést (pokud možno jednoduchý)  návod, jak
      psát programy snadno použitelné v různých jazykových mutacích.

          Prvním  předpokladem pro správné  chování  takového  programu je
      fungující   lokalizace.  Většina  současných  distribucí  Linuxu  je
      vystavěna nad knihovnami glibc, které by již s lokalizací neměly mít
      problémy. Zda je tomu  skutečně  tak, lze  zkontrolovat  pohledem do
      adresáře /usr/share/locale, který by měl obsahovat podadresář cs_CZ*
      (na mém počítači je to cs_CZ.ISO-8859-2) a v něm soubory LC_COLLATE,
      LC_CTYPE,  LC_MONETARY,  LC_NUMERIC,  LC_TIME a adresář LC_MESSAGES.
      Pokud  tomu  tak  není,  ale v systému  existuje  alespoň  definiční
      soubor  cs_CZ  (bývá   standardně   zahrnut  do  balíku  glibc  jako
      /usr/share/i18n/locale/cs\_CZ), je možné lokalizační  soubory pomocí
      příkazu

       localedef -i cs_CZ -f ISO-8859-2 cs_CZ.ISO-8859-2

          vygenerovat.     Pokud    systém     nevlastní    ani    uvedený
      definiční    soubor,  je   vhodné    začít    například   na  adrese
      ftp://ftp.fi.muni.cz/pub/localization/locale,   kde   najdete   více
      informací :-).

          Nyní přistupme k tvorbě vlastního  programu  lcexample.  Nejprve
      je potřeba  program  přimět, aby  používal  lokalizaci; to provedeme
      pomocí funkce setlocale(LC\_ALL, "") (viz funkce init()) - parametry
      uvedené v příkladu  sdělí  programu,  aby  použil  nastavení  určené
      proměnnými prostředí LC_* a LANG. Dále již stačí používat standardní
      knihovní funkce pro práci s lokalizovaným prostředím.

          Pro jednotlivé kategorie jsou k dispozici následující funkce:

     LC_COLLATE - lexikografické třídění (viz funkce lc_collate()).

      - int strcoll(const char *s1, const char *s2) - porovnávání  řetězců
        s ohledem na lokalizaci;  syntaxe a návratové  hodnoty  odpovídají
        funkci strcmp().

      -  size_t   strxfrm(char   *dest,  const  char  *src,  size_t  n)  -
         transformace  řetězce  tak, aby porovnání  dvou  takto  vzniklých
         řetězců  pomocí  strcmp() mělo  stejný  výsledek,  jako porovnání
         původních pomocí strcoll().

      LC_CTYPE  -  rozdělení   znaků  do  tříd  (malá  a  velká   písmena,
         oddělovače, čísla, bílá místa, ...) (viz funkce lc_ctype()).

      - int isalpha (int c) - písmena

      - int isascii (int c) - 7-bitová unsigned char hodnota z ASCII.

      - (podobně  iscntrl(),  isdigit(),  isgraph(), islower(), isprint(),
        ispunct(), isspace(), isupper(), isxdigit())

      LC_TIME - časové údaje (viz funkce lc_time()).

      - size_t  strftime(char *s, size_t  max, const char  *format,  const
        struct tm *tm) - nahradí  sekvence  %x v řetězci  format  časovými
        údaji  podle  časové  zóny a zvolené  lokalizace.  Např.  %A = den
        v týdnu,  %a = zkratka  dne v týdnu, %B = měsíc,  ...  "Nahrazuje"
        funkce ctime(), asctime().

      LC_NUMERIC - formátování čísel (viz funkce lc_numeric()).

      - int printf(const char  *format, ...) - pokud  %-sekvence pro čísla
        obsahují  apostrof (např. %'d) vytiskne oddělovač desetinných míst
        a tisícovek s ohledem na lokalizaci (nefunguje až v libc.5).

      LC_MONETARY   -  formátování   peněžních   informací   (viz   funkce
         lc_monetary()).

      - ssize_t  strfmon(char *s, size_t maxsize, const char *format, ...)
        - nahradí  sekvence %x peněžními údaji  podle zvolené  lokalizace.
        Ve formátovacím  řetězci lze zadat: %i = mezinárodní  symbol  měny
        s  peněžní  částkou  (CZK),  %n = národní  symbol  měny s  peněžní
        částkou (Kč). Peněžní  částky musí být čísla typu double nebo long
        double. O této  funkci  se bohužel v info  dokumentaci  nedozvíte,
        navíc v libc.5 tato funkce vůbec nebyla. Více informací  naleznete
        v souboru strfmon.man3.


          Pokud  by  pro  některou  z  kategorií  neexistovaly  standardní
      funkce,  je možné  formátování  udělat  "ručně" za pomocí  struktury
      lconv získané z funkce localeconv().

          Samostatnou   kapitolu  pak  tvoří  překlad  (nejen   chybových)
      zpráv  programu. V systémech  s libc.6  (glibc)  se  provádí  pomocí
      balíku gettext. (Ve starších  systémech s libc.5, případně na jiných
      operačních systémech se k tomuto účelu používají funkce gencat; více
      viz ftp://ftp.fi.muni.cz/pub/linux/local/czech-howto/.)

          To, že program  má  používat  katalog s českými  překlady  zpráv
      (kategorie  LC_MESSAGES),  už  akceptoval  při  volání  setlocale();
      je ovšem  potřeba ještě specifikovat  jméno  katalogu. To uděláme na
      začátku programu  voláním funkcí bindtextdomain(package,  localedir)
      -  cesta  ke  katalogu  - a  textdomain(package)  - jméno  katalogu,
      který používáme (viz funkce init()). Všechny řetězce, které mají mít
      svůj ekvivalent v katalogu, je nutné "prohnat" funkcí gettext() (viz
      funkce invite()).

          Ještě  zbývá  napsat  české překlady  zpráv.  Nejprve je potřeba
      pomocí programu  xgettext  vygenerovat  katalog  obsahující  všechny
      originální zprávy. Ve vzorovém programu byl k tomu použit příkaz

       xgettext --default-domain=lc_example --output-dir=. --add-comments
        --keyword=_ lc_example.c

          kde
      --default-domain=lc_example    --   ulož    výsledek    do   souboru
         lc_example.po
      --output-dir=. -- cílový adresář
      --add-comments -- napiš, kde se jednotlivé řetězce vyskytují
      --keyword=_ -- místo gettext použij klíčové slovo _

          Nyní  lze ke každému  řetězci  připsat za klíčové  slovo  msgstr
      příslušný  překlad.  Pokud by váš katalog byl větší je velmi výhodné
      používat na překlad  Emacs v tzv.  po-módu  (potřebný  kód do Emacsu
      je přiložen v balíku  gettext).  Hlavní výhoda je, že po-mode umí na
      požádání zobrazit přímo místo zdrojového kódu, kde se zpráva nachází
      (klávesa s). Dále umožňuje kontrolu, zda jsou zprávy dobře přeloženy
      (zda sedí počet  formátovacích  značek, zda zprávy  začínají a končí
      novým řádkem stejně), automatické vyplnění úvodní hlavičky i s datem
      revize (vše Shift-v). Samozřejmě editovat překlad lze pomocí klávesy
      Enter,  ukončení  Ctrl-c  Ctrl-c.  Pokud  se  vám v katalogu  objeví
      zpráva  označená jako  "fuzzy", pak by tato zpráva  měla být neúplně
      přeložena. Toto označení lze odstranit klávesou Tab. Zprávu lze jako
      "fuzzy" označit pomocí BackSpace.

          Pro  instalaci  po-módu je třeba  umístit  soubor  po-mode.el} (
      po-mode.elc) do  adresáře  /usr/share/emacs/site-lisp a následně  do
      souboru HOME/.emacs přidat:

      (setq auto-mode-alist
        (cons '("\\.po[tx]?\\'\\|\\.po\\." . po-mode)\
        auto-mode-alist))
      (autoload 'po-mode "po-mode")

          Máme tedy přeložen celý katalog a pomocí

       msgfmt -v -o lc_example.mo lc_example.po

          ho  přeložíme  do binární  podoby.  Pak už stačí  jen  přesunout
      lc_example.mo   do  adresáře   /usr/share/locale/cs_CZ*/LC_MESSAGES,
      zkompilovat program a vyzkoušet.

       gcc -o lc_example lc_example.c

       LC_ALL=cs_CZ.ISO-8859-2 ./lc_example
      Dobrý den!
      Setříděná slova: cikáda chroust pláně \
              plaňka Plášil
      Úterý - to nesnáším!
      Čísla: 12 345 678   1 234 567,125000
      Napište nějaký text, prosím:
      Umíš česky, ježku?
      ++++_+++++-_+++++-_
      Zapomněl jste zaplatit  1 234,00Kč autorovi \
              tohoto programu.

       LC_ALL=en_US ./lc_example
      Hello!
      Sorted words: chroust cikáda Plášil \
              pláně plaňka
      Tuesday - I hate it!
      Numbers: 12,345,678   1,234,567.125000
      Wrote some text, please:
      Umíš česky, ježku?
      ++--_-++++-_++-++-_
      You have forgotten to pay  1,234.00 to the \
              author of this program.

          Je vidět, že funkce  strfmon()  zatím  není  dokonalá  (zbytečná
      mezera  na  začátku,  mezi  částkou a Kč není  mezera,  ač by  podle
      definice v souboru  cs_CZ být měla),  stejně  jako  funkce  printf()
      (desetinná čísla  nejsou  dělena do skupin). Časem se ale tyto chyby
      určitě vyladí.

      A na závěr ještě jedna užitečná funkce, která se může při lokalizaci
      hodit a to

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

          Tato  funkce  vrátí  hodnotu,  kterou  po ní  požadujeme - např:
      první  den  v  týdnu  nl_langinfo(DAY_1)  nebo  první  měsíc  v roce
      nl_langinfo(MON_1).  Argumenty  které  můžete  požadovat   naleznete
      v souboru /usr/include/langinfo.h.


            výheň