Jak zrobiæ ustawienia lokalne w Linux-ie. (Locales)

Peeter Joot, peeter_joot@vnet.ibm.com
v1.5, 21 Lipca 1997.
Wersja polska: Bartosz Maruszewski B.Maruszewski@jtz.org.pl
v1.4, 4 Listopada 1997


Dokument ten opisuje jak skonfigurowaæ twojego Linux-a, aby móc używaæ pakietu "Locales", czyli ustawieñ narodowych. Dokument ten jest napisany w standardzie ISO-8859-2. Wersja oryginalna znajduje siê pod adresem ftp.icm.edu.pl. Nowsza wersja zawiera uaktualnione wskazania na katalog zawierający jądra na ftp.icm.edu.pl

1. Wprowadzenie.

To jest naprawdê opis tego co musiałem zrobiæ, żeby działał u mnie pakiet "Locales". Zrobiłem to tylko dla zabawy, i pomyślałem, że może niektórzy chcą sami spróbowaæ. Jak już raz wszystko poustawiasz, powinieneś móc używaæ aplikacji korzystających z NLS, wraz z twoimi lokalnymi ustawieniami. Już niedługo pakiet "Locales" powinien byæ czêścią standardowych dystrybucji i wiêkszośæ z tego co tu napisałem bêdzie niepotrzebne.

2. Co to w ogóle jest pakiet "Locales"?

Jest to pakiet, który zawiera ustawienia specyficzne dla twojego kraju. (zapis daty, czasu, specjalne litery itp.) Ustawieñ tych nie powineneś zapisywaæ na stałe w swoich programach.

Jeśli masz na swoim komputerze zainstalowane ustawienia lokalne dla różnych krajów/jêzyków, to poprzez nastêpujące zmienne możesz kontrolowaæ zachowanie programów korzystających z nich. Ustawieniem domyślnym jest ustawienie wg. standardu C lub POSIX zapisane na stałe w "libc".

LANG

-- ustawia jakiego jêzyka używamy; może byæ zmienione przez zmienną LC_xxxx,

LC_COLLATE

-- ustawia porządek sortowania,

LC_CTYPE

-- definicje znaków, duże i małe litery... używane jest to przez takie funkcje jak: toupper, tolower, islower, isdigit itp.

LC_MONETARY

-- definicja formatu liczb związanych z pieniêdzmi. Są tu definicje separatora tysiêcy, separatora ułamkowego, symbolu pieniądza (zł) i miejsce, gdzie należy go umieściæ,

LC_NUMERIC

-- separatory tysiêcy i ułamkowe oraz grupowanie numeryczne,

LC_TIME

-- definicja formatu czasowego i datowego. Są tu zdefiniowane np. dni tygodnia, miesiące,

LC_MESSAGES

-- wyrażenia Nie i Tak,

LC_ALL

-- ustawia jakiego jêzyka używamy i zmienia wszystkie inne zmienne LC_xxxx.

Oto niektóre ustawienia, a jest ich znacznie wiêcej:

en_CA

-- kanadyjski angielski,

en_US

-- amerykañski angielski,

de_DE

-- niemiecki niemiecki,

fr_FR

-- francuski francuski.

Jeśli piszesz program i chcesz, aby można go było używaæ na całym świecie respektuj ustawienia lokalne. Najważniejszym powodem jest to, że nie każdy bêdzie używał tego samego zestawu znaków czy strony kodowej co ty.

Upewnij siê, że nie robisz czegoś takiego w swoich programach:

   /* sprawdź czy to litera */
   if ( (( c >= 'a') && ( c <= 'z' )) ||
        (( c >= 'A') && ( c <= 'Z' )) ) { ... }

Jeśli napiszesz coś takiego, to zakładasz, że użytkownik bêdzie używał tylko podstawowych znaków z kodu ASCII i nie bierzesz pod uwagê, iż może używaæ strony kodowej specyficznej dla swojego kraju. Pomija to takie znaki jak np. a-umlaut, które zostałoby użyte w środowisku niemieckim. Zamiast tego powinieneś raczej używaæ funkcji, które respektują lokalne ustawienia, jak np. isalpha(). Jeśli twój program wyraźnie wymaga tylko podstawowych znaków ASCII (US-ASCII), dalej używasz funkcji isalpha(), ale musisz także ustawiæ zmienną LANG albo LC_CTYPE albo LC_ALL na "C" lub użyæ funkcji setlocale(LC_CTYPE, "C").

Ustawienia lokalne pozwalają na dużą elastycznośæ i robią pewne założenia, o których programista mógł zapomnieæ.

Na przykład nie możesz z góry założyæ pozycji danego znaku na stronie kodowej. Nic nie stoi na przeszkodzie, żebyś np. stworzył sobie stronê kodową, na której "A" byłoby na pozycji 99 a nie 65.

Podstawową ideą jest to, że różni ludzie mówią różnymi jêzykami, przetsrzegają różnych reguł sortowania, używają różnych stron kodowych i mieszkają w różnych krajach. Ustawienia lokalne i funkcje, które ich przstrzegają dają środki na respektowanie takich rzeczy i odpowiedniego ich traktowanie. Nie wymaga to dużego nakładu pracy, tylko trochê innego sposobu myślenia podczas pisania takich programów.

3. Uwagi.

4. Czego potrzebujesz.

Kilka rzeczy musisz sobie ściagnąæ. Wszystko czego potrzebujesz znajdziesz na ftp.icm.edu.pl Kiedy ja instalowałem u siebie ustawienia lokalne używałem libc-5.2.18, która jest teraz troche przestarzała. Jak na razie powiedziano mi, że bieżącą wersją jest 5.4.17 i taką też umieszczam w opisie. Przypuszczalnie libc-5.4.17 bêdzie przestarzała zanim zdążysz mrugnąæ wiêc po prostu użyj najnowszej dostêpnej wersji.

Rozważ użycie glibc (gnu libc) zamiast Linux libc 5 w pracach unaradawiających (internalizacyjnych). Glibc jest w pełni unarodowiona i ma pełne wsparcie dla programowania narodowego tak samo jak jest w pełni przenaszalna FIXME i ma wbudowaną obsługê wątków. Prawie cała internalizacja zrobiona w libc 5 wziêta zostałą z glibc. Lokalizacje i mapy klawiszy dla glibc są powiązane z dodatkami o lokalizacjami glibc.

Jeśli jesteś za używaniem glibc, to możesz pominąæ to mini-howto. Dołączanie dodatków lokalizacyjnych do kompilacji i instalacji glibc jest trywialne i opisane jest w dokumentacji do instalacji glibc.
ednak ostrzegam, że pełna aktualizacja nie jest trywialnym zadaniem! Mam nadziejê, że RedHat (którego używam) wypuści niedługo dystrybucjê opartą na glibc, bo jakoś nie uśmiecha mi siê rekompilacja całego mojego systemu. Wszystkie poniższe pakiety możesz znaleźæ pod adresem ftp.icm.edu.pl w katalogu /pub/Linux/sunsite/GCC oprócz "make" - /pub/Linux/sunsite/devel/make oraz jądra - /pub/Linux/kernel/

5. Instalacja wszystkiego.

Oto co zrobiłem, aby wszystko zainstalowaæ. Miałem już system ELF (kompilator, jądro ...).

  1. Najpierw zainstalowałem pakiet binutils: tar xzf binutils-2.6.0.2.bin.tar.gz -C /
  2. Potem zainstalowałem bibliotekê do łączenia:
       tar zxf ld.so-1.7.12.tar.gz -C /usr/src
       cd /usr/src/ld.so-1.7.12  
       sh instldso.sh
    
  3. Potem binaria do libc. Przeczytaj odpowiednie pliki dotyczące instalacji w release.libc-5.4.17.
       rm -f /usr/lib/libc.so /usr/lib/libm.so
       rm -f /usr/include/iolibio.h /usr/include/iostdio.h
       rm -f /usr/include/ld_so_config.h /usr/include/localeinfo.h
       rm -rf /usr/include/netinet /usr/include/net /usr/include/pthread
       tar -xzf libc-5.4.17.bin.tar.gz -C /
    
  4. Teraz trzeba uruchomiæ ldconfig, żeby zlokalizowaæ nowe biblioteki dzielone: ldconfig -v.
  5. Jest błąd, który został już poprawiony w libc. Powodował on złe działanie "make" i innych programów. Oto co zrobiłem, żeby skompilowaæ i zainstalowaæ "make":
       tar zxf make-3.74.tar.gz -C /usr/src
       cd /usr/src/make-3.74
       patch < ścieżka_do_release.libc-5.4.17
       configure --prefix=/usr
       sh build.sh
       ./make install
       cd ..
       rm -rf make-2.74
    
  6. Teraz można skompilowaæ i zainstalowaæ localedef:
       mkdir /usr/src/libc
       tar zxf libc-5.4.17.tar.gz -C /usr/src/libc
       cd /usr/src/libc
       cd include
       ln -s /usr/src/linux/include/asm .
       ln -s /usr/src/linux/include/linux .
       cd ../libc
       ./configure
       make clean ; make depend
       cd locale
       make programs
       mv localedef /usr/local/bin
       mv locale /usr/local/bin
    
  7. Umieśæ zestawy znaków tam, gdzie je znajdzie localedef. Ja użyłem zestawów znaków i locales z ftp.dkuug.dk (charmaps.tar i locales.tar). W Polsce dostêpne są pod adresem: ftp.arch.pwr.wroc.pl w katalogu /mirror/linux/nls/locale/dkuug. Starsze "localedef" (5.2.18) szukało źródeł zestawów znaków w katalogu /usr/share/nls/charmap, ale teraz "localedef" szuka ich w katalogu /usr/share/i18n/charmaps oraz źródeł definicji lokalnych w /usr/share/i18n/locales
       mkdir /usr/share/i18n
       mkdir /usr/share/i18n/charmaps
       mkdir /usr/share/i18n/locales
       tar xf charmaps.tar -C /usr/share/i18n/charmaps
       tar xf locales.tar -C /usr/share/i18n/locales
    

    Nowsze "localedef" (5.4.17) są sprytniejsze i szukają zbiorów źródłowych definicji lokalnych podczas obsługi funkcji "copy". Podczas, gdy starsze "localedef" musiały mieæ już utworzone pliki źródłowe definicji lokalnych, aby obsłużyæ funkcjê "copy". Poniższa lista poleceñ ma wysortowane zależności i może byæ użyta, aby wygenerowaæ wszystkie objekty lokalne w zaleźności od używanej wersji biblioteki libc, ale powinieneś móc teraz utworzyæ tylko te, które chcesz.

       localedef -ci en_DK -f ISO_8859-1:1987 en_DK
       localedef -ci sv_SE -f ISO_8859-1:1987 sv_SE
       localedef -ci fi_FI -f ISO_8859-1:1987 fi_FI
       localedef -ci sv_FI -f ISO_8859-1:1987 sv_FI
       localedef -ci ro_RO -f ISO_8859-1:1987 ro_RO
       localedef -ci pt_PT -f ISO_8859-1:1987 pt_PT
       localedef -ci no_NO -f ISO_8859-1:1987 no_NO
       localedef -ci nl_NL -f ISO_8859-1:1987 nl_NL
       localedef -ci fr_BE -f ISO_8859-1:1987 fr_BE
       localedef -ci nl_BE -f ISO_8859-1:1987 nl_BE
       localedef -ci da_DK -f ISO_8859-1:1987 da_DK
       localedef -ci kl_GL -f ISO_8859-1:1987 kl_GL
       localedef -ci it_IT -f ISO_8859-1:1987 it_IT
       localedef -ci is_IS -f ISO_8859-1:1987 is_IS
       localedef -ci fr_LU -f ISO_8859-1:1987 fr_LU
       localedef -ci fr_FR -f ISO_8859-1:1987 fr_FR
       localedef -ci de_DE -f ISO_8859-1:1987 de_DE
       localedef -ci de_CH -f ISO_8859-1:1987 de_CH
       localedef -ci fr_CH -f ISO_8859-1:1987 fr_CH
       localedef -ci en_CA -f ISO_8859-1:1987 en_CA
       localedef -ci fr_CA -f ISO_8859-1:1987 fr_CA
       localedef -ci fo_FO -f ISO_8859-1:1987 fo_FO
       localedef -ci et_EE -f ISO_8859-1:1987 et_EE
       localedef -ci es_ES -f ISO_8859-1:1987 es_ES
       localedef -ci en_US -f ISO_8859-1:1987 en_US
       localedef -ci en_GB -f ISO_8859-1:1987 en_GB
       localedef -ci en_IE -f ISO_8859-1:1987 en_IE
       localedef -ci de_LU -f ISO_8859-1:1987 de_LU
       localedef -ci de_BE -f ISO_8859-1:1987 de_BE
       localedef -ci de_AT -f ISO_8859-1:1987 de_AT
       localedef -ci sl_SI -f ISO_8859-2:1987 sl_SI
       localedef -ci ru_RU -f ISO_8859-5:1988 ru_RU
       localedef -ci pl_PL -f ISO_8859-2:1987 pl_PL
       localedef -ci lv_LV -f BALTIC lv_LV
       localedef -ci lt_LT -f BALTIC lt_LT
       localedef -ci iw_IL -f ISO_8859-8:1988 iw_IL
       localedef -ci hu_HU -f ISO_8859-2:1987 hu_HU
       localedef -ci hr_HR -f ISO_8859-4:1988 hr_HR
       localedef -ci gr_GR -f ISO_8859-7:1987 gr_GR
    

6. I co teraz?

Po przejściu omówionych powyżej kroków powinieneś móc używaæ ustawieñ lokalnych na swoim komputerze. Oto prosty przykładowy program.

 
   /* test.c : prosty przykładowy program do sprawdzenia czy
   ustawienia lokalne działają
   */
    #include <locale.h>
    #include <stdio.h>
    #include <time.h>
    
    main(){
            time_t t;
            struct tm * _t;
            char buf[256];

            time(&t);
            _t = gmtime(&t);
                                            

            setlocale(LC_TIME,"");
            strftime(buf,256,"%c",_t);
           
            printf("%s\n",buf);
    }

Żeby sprawdziæ jakie są bieżące ustawienia możesz użyæ programu "locale". Skompiluj powyższy program i uruchom z różnymi ustawieniami. gcc -s -o Test test.c Zobacz jakie są bieżące ustawienia: locale

   LANG=POSIX
   LC_COLLATE="POSIX"
   LC_CTYPE="POSIX"
   LC_MONETARY="POSIX"
   LC_NUMERIC="POSIX"
   LC_TIME="POSIX" 
   LC_MESSAGES="POSIX"
   LC_ALL=

   Hmmm... ustawienia standardowe C...
   No to zmieñmy na inne:
   
   export LC_TIME=en_CA -- kanadyjski angielski
   Test
   Sat 23 Mar 1996 07:51:49 PM
   
   A teraz francuski kanadyjski:
   export LC_TIME=fr_CA
   Test
   sam 23 mar 1996 19:55:27

7. Naprawa błêdu "catopen".

Instalacja obsługi ustawieñ lokalnych naprawia automatycznie błąd (a może zaletê) w poleceniu "catopen" w bibliotece libc. Powiedzmy, że napiszesz program, który używa komunikatów z katalogu /home/peeter/catalogs/de_DE.

Teraz - jeśli wykonasz nastêpujące polecenia nie mając zainstalowanych ustawieñ lokalnych dla de_DE,

   export LC_MESSAGES=de_DE
   export NLSPATH=/home/peeter/catalogs/%L/%N.cat:$NLSPATH

katalog z niemieckimi komunikatami nie zostanie otwarty. Funkcja catgets pobierze komunikaty z katalogu standardowego.

Dzieje siê tak ponieważ funkcja "catopen" wywołuje funkcjê "setlocale", żeby pobraæ odpowiednie komunikaty, a funkcja "setlocale" zwróci błąd pomimo tego, że została ustawiona zmienna środowiskowa. Nastêpnie funkcja "catopen" próbuje załadowaæ komunikaty zastêpując wszystkie "L" literą "C" w zmiennej NLSPATH.

Możesz nadal używaæ swojego katalogu z komunikatami bez definiowania ustawieñ lokalnych, ale musiałbyś bezpośrednio ustawiæ czêśæ "L" zmiennej NLSPATH:

   export NLSPATH=/home/peeter/catalogs/de_DE/%N.cat:$NLSPATH

ale to mija siê z celem zastosowania zmiennych ustawieñ lokalnych.

8. Pytania i odpowiedzi.

Sekcja ta mogłaby urosnąæ i zmieniæ siê w FAQ, ale jest jeszcze za mała.

8.1 msgcat

Używam Linux-a i napisałem taki program:

   --------------------------------------------------------------------
   #include <stdio.h>
   #include <locale.h>
   #include <features.h>
   #include <nl_types.h>
   
   main(int argc, char ** argv)
   {
           nl_catd catd;
           
           setlocale(LC_MESSAGES, "");
           catd = catopen("msg", MCLoadBySet);
           fprintf(stderr,catgets(catd, 1, 1, "otwarcie komunikatów lokalnych nie powiodło siê\n"));
           catclose(catd);
   }

   --------------------------------------------------------------------
   $ msg.m
   $set 1
   
   1 locale message pass\n
   --------------------------------------------------------------------

Jeśli użyjê bezwzglêdnej ścieżki dostêpu w "catopen", np.:

catopen("/etc/locale/msg.cat", MCLoadBySet);

To jest dobrze. Ale jeśli używam poprzedniego przykładu "catopen" zwraca mi -1 (czyli, że siê nie powiodło)

Czêściowa odpowiedź na to pytanie jest w poprzedniej sekcji, ale podam jeszcze trochê dodatkowych informacji.

Jest wiele "odpowiednich miejsc", gdzie możesz umieściæ katalog z komunikatami. Nawet gdybyś nie ustawił zmiennej środowiskowej NLSPATH, to jest ona nastêpująco zdefiniowana w libc:

   $ strings /lib/libc.so.5.4.17 | grep locale | grep %L
   /etc/locale/%L/%N.cat:/usr/lib/locale/%L/%N.cat:/usr
   /lib/locale/%N/%L:/usr/share/locale/%L/%N.cat:/usr/
   local/share/locale/%L/%N.cat

Wiêc jeśli zrobiłeś jedno z poniższych:

   $ export LC_MESSAGES=en_CA
   $ export LC_ALL=en_CA
   $ export LANG=en_CA

to funkcja catopen("msg", MCLoadBySet); działałaby, gdyby twój katalog z komunikatami był jednym z:

   /etc/locale/en_CA/
   /usr/lib/locale/en_CA/
   /usr/lib/locale/msg/
   /usr/share/locale/en_CA/
   /usr/local/share/locale/en_CA/

Jednak to nie zadziała jeśli nie masz zainstalowanych ustawieñ lokalnych dla en_CA, ponieważ funkcja "setlocale" nie powiedzie siê i w wywołaniu funkcji "catopen" w miejsce "L" zostanie podstawiona litera "C".

9. Zakoñczenie.

To tyle. Mam nadziejê, że ten "podrêcznik" pomógł ci chociaż trochê. Jest zapewne dużo miejsc, gdzie możesz szukaæ dodatkowych informacji na temat pisania programów zgodnych z ustawieniami lokalnymi. Założê siê, że jeśli poszukasz trochê po sieci (WWW), to znajdziesz dużo informacji. Ulrich Drepper, który zaimplementował wiêkszośæ kodu umiêdzynarodowiającego, ma troche informacji na swojej stronie WWW; możesz tam zacząæ. Jest także trochê informacji na stronach informacyjnych i w podrêczniku systemowym "man" o libc.

9.1 Od tłumacza.

Jeśli znalazłeś jakieś rażące błêdy ortograficzne, gramatyczne, składniowe, techniczne to pisz do mnie:

B.Maruszewski@jtz.org.pl

Oficjalną stroną tłumaczeñ HOWTO jest http://www.jtz.org.pl/

Aktualne wersje przetłumaczonych dokumentów znajdują siê na tejże stronie. Dostêpne są także poprzez anonimowe ftp pod adresem ftp.jtz.org.pl/HOWTO/

Przetłumaczone przeze mnie dokumenty znajdują siê także na mojej stronie WWW. Są tam też odwołania do Polskiej Strony Tłumaczeniowej.

Kontakt z naszą grupą, grupą tłumaczy możesz uzyskaæ poprzez listê dyskusyjną jtz@ippt.gov.pl. Jeśli chcesz sie na nią zapisaæ, to wyślij list o treści subscribe jtz Imiê Nazwisko na adres majordomo@ippt.gov.pl

Zmiany wprowadzone przeze mnie do tego dokumentu to polskie odnośniki do serwerów ftp i WWW.
W wersji v1.31 została poprawiona nazwa pliku testowego z "test" na "Test", co jak zauważył jeden z czytelników ma podstawowe znaczenie ponieważ program "test" już istnieje w systemie.