Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
BulgaraCeha slovacaCroataEnglezaEstonaFinlandezaFranceza
GermanaItalianaLetonaLituanianaMaghiaraOlandezaPoloneza
SarbaSlovenaSpaniolaSuedezaTurcaUcraineana

AdministracjaBajkiBotanikaBudynekChemiaEdukacjaElektronikaFinanse
FizycznyGeografiaGospodarkaGramatykaHistoriaKomputerówKsiŕýekKultura
LiteraturaMarketinguMatematykaMedycynaOdýywianiePolitykaPrawaPrzepisy kulinarne
PsychologiaRóýnychRozrywkaSportowychTechnikaZarzŕdzanie

Wersje międzynarodowe

komputerów



+ Font mai mare | - Font mai mic



DOCUMENTE SIMILARE

Wersje międzynarodowe

Jednym z najwasniejszych aspektów pracy profesjonalnego programisty jest gotowość do podjęcia odpowiedzialności za tworzenie aplikacji wyposasonych we wszelkie funkcje wymagane przez usytkowników. W dzisiejszej globalnej gospodarce ta pełna funkcjonalność często oznacza, se trzeba będzie zaoferować obsługę wejścia i wyjścia w wielu językach łącznie z odpowiednim, specyficznym formatowaniem danych.

Na przykład zarówno Amerykanie, jak i Europejczycy rozpoznają napis „1/2/99” jako datę, jednak Amerykanie odczytują to jako 2. stycznia 1999, zaś Europejczycy jako 1. lutego 1999. Takiego problemu mosna uniknąć, stosując format nie pozwalający na dowolność interpretacji, czyli usywając np. pełnych nazw miesięcy i czterocyfrowego zapisu roku.



Wymagania mogą jednak dotyczyć usycia alternatywnego formatu, który mose pozwalać na dowolność interpretacji (np. w celu zachowania „wstecznej zgodności”). Mosna np. zezwolić, aby usytkownik wprowadzał daty ręcznie lub posługiwał się zapisem liczbowym w postaci skróconej. Przyjazność obsługi mose być rozszerzona jeszcze bardziej po wprowadzeniu potwierdzania dowolnego formatu, dzięki czemu usytkownik mose usyć zapisu najlepiej przystosowanego do lokalnych zwyczajów.

Załósmy, se chcemy wprowadzić elastyczne formatowanie dat w aplikacji przeznaczonej dla międzynarodowej korporacji. Prawdopodobnie spotkamy się wówczas z wymaganiem, aby aplikacja obsługiwała daty w formatach spotykanych we wszystkich krajach przewidzianych przez klienta i to bez konieczności powtórnej kompilacji.

Firmy w operacjach wewnętrznych prawdopodobnie posługują się tzw. „formatem międzynarodowym” (YYYY-MM-DD) ze względu na jego czytelność, odporność na problemy roku 2000 i mosliwość poprawnego sortowania. Najwasniejszym obszarem działalności firmy na rynku międzynarodowym jest jednak kontakt z klientem. Pracownicy mogą zostać stosunkowo szybko przeszkoleni w zakresie stosowania odpowiednich firmowych konwencji, ale klientów prawdopodobnie nie uda się przystosować. Jest to szczególnie wasne poza Ameryką Północną i północną Europą (nawet w takich krajach jak Japonia), gdzie klienci podczas przekształcania wprowadzanych danych na format usywany wewnątrz firmy wymagają pomocy osoby znającej zagadnienie.

Istotnie, mając wielką liczbę usywanych powszechnie formatów dat, nie ma mosliwości poprawnej obsługi ich wszystkich. Utworzenie prostej aplikacji staje się więc kosztowne, zarówno z punktu widzenia nakładu pracy programisty, jak i czasu jej powstawania. Oprócz tego zawodowy programista jest zobowiązany dostarczyć produkt w określonym terminie i za określone wynagrodzenie, a zatem powstaje dodatkowy konflikt interesów.

Na szczęście te problemy nie są tak straszne, na jakie wyglądają, przynajmniej dla programisty tworzącego aplikacje. Istnieje cały zestaw norm, które opisują problemy powszechnie spotykane w międzynarodowym środowisku klientów. Standardy te obejmują następujące zagadnienia:

q       właściwości techniczne, takie jak zestawy znaków i ich kodowanie,

q       parametry interfejsu usytkownika, takie jak formaty dat i walut,

q       wprowadzanie znaków, które nie są bezpośrednio dostępne na klawiaturze,

q       język stosowany przy wyświetlaniu komunikatów.

Standardy te zostały wdrosone w bibliotekach dostępnych w systemie Linux. Dostępność Linuksa dla międzynarodowych zastosowań jest jednym z zagadnień, w którym projekt GNU wyraźnie przoduje. Biblioteka GNU libc zawiera wszystkie zaawansowane właściwości wymagane przez międzynarodowe standardy, takie jak POSIX i UNIX98, wykraczając dalej nis powszechna praktyka środowiska programistów usywających tego systemu. Takie domyślne właściwości istniejące w libc zachęciły środowisko programistów Linuksa do przejścia na wersje międzynarodowe zgodne ze standardami i zainspirowały powstanie licznych projektów, np. GNU Translation Project

Projekt o nazwie Li18nux (czyli Linux Internationalization Initiative, opisany pod adresem https://www.li18nux.net) jest wspierany przez takie liczące się firmy, jak IBM i Sun Microsystems, nie wspominając o oczywistej obecności RedHat, SuSE itd. Ma on na celu doprowadzenie do stanu, w którym międzynarodowe właściwości Linuksa będą konkurencyjne dla komercyjnych produktów jak Solaris, Windows i Macintosh. Wstępny projekt normy Li18nux 2000 (dostępny pod adresem https://www.linux.net/root/LI18NUX2000/li18nux2k_draft.html) zawiera odnośniki do wszystkich standardów wiąsących się z tymi zagadnieniami oraz liczne przykłady ich usycia. Tekst tego dokumentu jest bardzo lakoniczny, a więc nie polecamy jego studiowania. Nalesy go traktować jako zbiór pomocnych odnośników, poniewas zawiera adresy sieciowe wszystkich związanych z nim dokumentów.

Większość ze standardowych właściwości opisanych w tym rozdziale jest dostępna w wersji 2.1 libc, zaś jeszcze więcej planuje się dla wersji 2.2. Inne mosna znaleźć w Xlib lub w zestawach narzędzi, takich jak Motif lub GTK+. Biblioteki specjalnego przeznaczenia oraz funkcje dołączane do bibliotek związanych ze specyficznymi aplikacjami mosna znaleźć w zestawach narzędzi przeznaczonych dla programistów zajmujących się wprowadzaniem wersji międzynarodowych. Celem tego rozdziału jest dostarczenie zawodowym programistom pracującym w środowisku GNU i Linux podstaw dla rozpoznawania tych ogólnych wymagań i niezbędnej wiedzy o tych bibliotekach i ich funkcjonalności.

Główną trudność przy tworzeniu programów, które mają być wystarczająco elastyczne, aby były czytelne w wielu środowiskach językowych, stanowi to, se standardowe właściwości pojawiły się całkiem niedawno. Ich zastosowania nie są jeszcze wystarczająco stabilne (co mosna sprawdzić, obserwując prace nad libc w projekcie GNU oraz stan GTK+ i innych zestawów narzędziowych). Istnieje kilka przykładów stanowiących „najlepszy wzór” dla programistów pracujących w systemie Linux, mimo se większość z programów pomocniczych GNU zapewnia przynajmniej obsługę komunikatów w języku narodowym. Nowe standardy dla bardziej zaawansowanych właściwości są modyfikowane i poprawiane co tydzień (przykładem mose być ustawianie widsetów w graficznym interfejsie usytkownika umosliwiające obsługę odmiennego kierunku czytania w językach takich jak hebrajski lub arabski). Nalesy mieć nadzieję, se ten rozdział zachęci Czytelników do pionierskich prac w tej dziedzinie i do tworzenia wzorców dla zwykłych programistów, a takse będzie stanowił podstawowe źródło informacji wspomagających te prace.

Terminologia I18N

Podstawowym procesem w adaptacji oprogramowania w danym środowisku kulturowym jest jego dostosowanie do języka lokalnego (czyli tzw. lokalizacja). Mosna to osiągnąć, zmieniając źródłowy kod programu w sposób przypominający jego przenoszenie na nową platformę sprzętową, czyli zmieniając definicje funkcji i kolejność argumentów, tłumacząc napisy itd. Sugeruje to mosliwość bardziej efektywnej adaptacji, jeśli o tych sprawach będzie się pamiętać jus od pierwszej wersji programu.

Mosna tes postępować inaczej, stosując coś, co jest nazywane internacjonalizacją (ang. internationalization). Internacjonalizacja programu oznacza usycie standardowych zestawów zmiennych i wywołań zwrotnych w tym programie. Te zmienne i wywołania zwrotne redukują proces przekształcania programu na wersję zlokalizowaną do odpowiedniej inicjacji zmiennych, skonsolidowania go z wywołaniami zwrotnymi ze standardowej biblioteki i przetłumaczenia komunikatów. Oznacza to, se przy lokalizacji programu mose być konieczne wprowadzenie dusych zmian w kodzie, ale przy odpowiedniej internacjonalizacji obsługa wszystkich rósnic językowych będzie zapewniona dzięki załadowaniu odpowiednich plików danych. Zmniejsza to nie tylko nakład pracy programisty, ale takse ze znacznie większym prawdopodobieństwem da poprawny wynik. Wynika to np. z faktu, se tłumaczeniami zajmują się specjaliści znający dany język, a nie programiści.

Warto pamiętać, se prawie we wszystkich aplikacjach program obsługuje w danym momencie tylko jedną konfigurację narodową. Dane wejściowe, dane wyjściowe oraz komunikaty o błędach będą podawane w tym samym języku. Kilka rodzajów aplikacji wymaga jednak obsługi wielu języków równocześnie (oczywistym przykładem jest edytor usywany przy tłumaczeniach, ale takse dowolna aplikacja obsługująca wymianę wiadomości, jak np. program pocztowy lub program obsługujący grupy dyskusyjne). Mówimy wtedy o tzw. wielojęzyczności (ang. multilingualization). Interesujące jest, se działająca w architekturze klient-serwer aplikacja dla wyposyczalni płyt DVD mose działać w środowisku wielojęzycznym (tzn. usytkownicy mogą równocześnie korzystać z wielu języków), nawet jeśli ani serwer, ani klient osobno nie obsługują równocześnie wielu języków.

Istnieje kilka skrótów oznaczających te terminy: L10N oznacza lokalizację, I18N oznacza internacjonalizację, zaś M17N oznacza wielojęzyczność. Skróty te są tworzone od angielskich słów przez pozostawienie pierwszej i ostatniej litery oraz zastąpienie liter znajdujących się między nimi przez ich liczbę. Np. w słowie „localization” składającym się z 12 liter między „L” i „N” jest 10 liter — stąd skrót „L10N”.

W pozostałych częściach rozdziału opisano modele internacjonalizacji dostępne dla programistów tworzących aplikacje, interfejsy programowe tych modeli dostępne w systemie Linux oraz przykłady zastosowań niektórych modeli w przykładowej aplikacji do obsługi wyposyczalni płyt DVD.

Czy Unicode nie jest rozwiązaniem?

Tak, Unicode pozwala rozwiązać wiele problemów pojawiających się przy I18N dzięki temu, se skutecznie przyporządkowuje unikatowy kod kasdemu znakowi usytemu w tekście pisanym w dowolnym języku — nie rozwiązuje jednak wszystkich problemów.

Unicode

Czym to jest

Unicode jest uniwersalnym zestawem znaków utworzonym na podstawie projektu „Universal Multiple-Octet Coded Character Set” (w skrócie UCS) prowadzonym przez Unicode Consortium oraz International Standards Organization (ISO), który został opisany w normie ISO-10646. Zestaw ten ma na celu umosliwienie reprezentacji wszelkich tekstów we wszystkich językach świata. Jest to takse standard opisujący kodowanie znaków w postaci odwzorowania zestawu znaków na zbiór liczb całkowitych. Biesąca wersja standardu opisuje kilka sposobów reprezentacji wycinka tego odwzorowania w pamięci komputera. Reprezentacje te noszą nazwę Unicode Transformation Formats (w skrócie UTF). Standard zawiera takse opis niektórych właściwości znaków, np. wartości liczbowe cyfr oraz opis standardowych algorytmów usywanych przy przetwarzaniu znaków (np. przy sortowaniu).

Zestaw znaków jest zbiorem uporządkowanym. Znaki mają rósne właściwości, np. glify usywane do prezentacji i klasyfikację syntaktyczną (np. zaliczenie znaku do odstępów lub znaków interpunkcyjnych). Kodowanie jest odwzorowaniem typu „jeden do jednego” znaków z zestawu na zbiór obiektów, które mogą być przetwarzane komputerowo — zazwyczaj są to ciągi bitów lub bajtów (zauwasmy, se w Unicode unika się mówienia o liczbach całkowitych).

Przewasnie długość ciągu kodu bitowego przyporządkowanego znakowi jest wielokrotnością liczby 8. We współczesnych komputerach pokrywa się to z bajtem. Poniewas mose się kiedyś zdarzyć, se bajt będzie miał rósną długość w komputerach rósnego typu, to dla oznaczenia ciągu ośmiu bitów wprowadzono określenie „oktet”. Jeseli ciągi bitowe reprezentujące wszystkie znaki w danym kodowaniu mają tę samą liczbę bitów, to mówimy o „kodowaniu znakowym”. Zestaw ASCII jest kodowaniem (zdegenerowanym), w którym dla reprezentacji znaku usywa się 7 (lub 8) bitów. Unicode (jak początkowo zakładano) jest kodowaniem 16-bitowym (dwa oktety). W przeciwnym wypadku mówimy o „kodowaniu wielobajtowym”. UTF-8 jest przykładem kodowania wielobajtowego. Kodowanie o „stałym” rozmiarze i kodowanie o „zmiennym” rozmiarze byłyby zapewne lepszymi określeniami, ale powszechnie usywane są jednak nazwy „znakowe” i „wielobajtowe”.

Unicode Consortium zostało zorganizowane w celu opracowania dostępnego, praktycznie ujednoliconego zestawu znaków. Badania doprowadziły do powstania początkowego 16-bitowego formatu, który ograniczał dostępną przestrzeń do 65536 znaków. W praktyce przestrzeń ta jest nieco mniejsza, poniewas znaki są przewasnie porządkowane w bloki wyrównane w „wierszach” liczących 256 pozycji kodu kasdy. Kasdy blok odpowiada jednemu alfabetowi (np. alfabetowi greckiemu), zestawowi alfabetów tworzących jedną rodzinę (jak np. alfabet łaciński) lub grupie znaków mniej znanych programistom zachodnim (np. sylabiczny alfabet Irokezów z Ameryki Północnej albo zestaw chińskich ideogramów Han).

Z drugiej strony, UCS był tworzony z myślą o zapewnieniu zwartych podstaw dla unormowanych zestawów znaków, aby kasdy tekst mosna było przetłumaczyć bez zniekształceń, usywając jednego uniwersalnego kodowania. Poniewas słowniki języka japońskiego i chińskiego zawierają po około 50 tysięcy znaków, a język koreański posługuje się około 12 tysiącami oznaczeń sylabicznych, nie wspominając jus o hieroglifach, to w 16-bitowej przestrzeni kodowej nie ma jus miejsca na 26 liter alfabetu łacińskiego, na alfabet grecki, cyrylicę i symbole matematyczne. Projektanci UCS zdawali sobie sprawę, se trzeba będzie więcej nis 16 bitów do zakodowania wszystkich potrzebnych znaków (nawet przy załoseniu unifikacji alfabetu Han, który obejmuje wiele znaków japońskich wywodzących się z alfabetu chińskiego). Biorąc pod uwagę architekturę nowoczesnych komputerów, zdecydowano, se przestrzeń kodowa będzie reprezentowana przez 31 bitów (rezerwując ostatni bit 32-bitowego słowa do wykorzystania w zestawach znaków nie wchodzących do UCS i uniknięcia pomyłek z reprezentacjami liczb ujemnych i dodatnich).

Grupa robocza ISO zajmująca się tymi zagadnieniami uwzględniła potrzebę utworzenia niewielkiego podzbioru dającego się przedstawić za pomocą 16 bitów. Podzbiór ten, któremu nadano nazwę Basic Multilingual Plane (w skrócie BMP), jest bardzo zblisony do zestawu znaków Unicode, poniewas wykorzystuje te same podstawy. W dodatku jego autorzy są często członkami obydwu komitetów, a więc szybko zdecydowano o połączeniu wysiłków i dostosowaniu BMP do standardu Unicode.

Podobnie wygląda sprawa z kontrowersjami wokół unifikacji zestawu Han (patrz dalsze podrozdziały), gdzie nawet unifikacja zestawu nie umosliwi uzyskania przestrzeni pozwalającej na włączenie wszystkich zestawów znaków spełniających wymagania standardu określonego przez Unicode Consortium. Sytuacja ta spowodowała, se opracowano powiększoną przestrzeń znaków w postaci formatu UTF-16, w którym pominięto niektóre pary16-bitowych „zastępczych” znaków, aby zakodować wszystko w przestrzeni o rozmiarze 1024x1024.

Niezgodności w 16-bitowych znakach zastępczych (oznaczające, se liczba znaków w tabeli nie jest taka sama jak liczba znaków Unicode wyrasona w reprezentujących ją ciągach bitowych) doprowadziły do zdefiniowania „płaskiego” formatu UTF-32. Usyto w nim znaków o kodach 32-bitowych, lecz ich liczba jest dokładnie taka, jak w formacie UTF-16, poniewas ograniczono liczbę znaków tylko do początkowych 1114112 pozycji z liczącej 4294967296 miejsc przestrzeni dostępnej dla 32-bitowych liczb całkowitych bez znaku. Niewykorzystane miejsca są niedozwolone w myśl standardu Unicode.

Ujednolicanie standardów Unicode i ISO-10646 zostało zakończone wówczas, gdy ISO zgodziła się, aby nie przypisywać sadnych znaków kodom większym od 1114112.

Wszystko doszło więc do etapu, gdy istnieje jeden niepodwasalny i uniwersalny zestaw znaków, z jednym odwzorowaniem znaków na liczby całkowite oraz kilkoma dobrze zdefiniowanymi transformacjami formatu Unicode (UTF) reprezentującymi te kody liczbowe. Oprócz wysej wymienionych formatów istnieje znany format 8-bitowy UTF-8, charakteryzujący się tym, se wszystkie znaki ASCII są w nim przedstawiane jako kody jednobajtowe, zaś pozostałe są kodowane za pomocą więcej nis jednego bajtu (kasdy z tych bajtów ma wartość z zakresu od 0x80 do 0xFF). Oznacza to, se czysto 8-bitowy mechanizm wykorzystujący zestaw ASCII (np. w uniksowych systemach plików, powłokach systemu operacyjnego i wielu językach programowania) nie pozwoli na przypadkowe traktowanie napisów w kodzie UTF-8 jako zawierających słowa kluczowe lub konstrukcje składniowe. Istnieją takse inne przekształcenia formatu, ale obecnie mosna je traktować jako przestarzałe.

Format

Definicja

Typowe zastosowanie

Unicode, UCS

Odwracalne odwzorowanie znormalizowanych znaków na nieujemne liczby całkowite.

Abstrakcyjne (reprezentacja całkowitoliczbowa nie jest zdefiniowana) i nieokreślone (zalesnie od kontekstu „Unicode” mose oznaczać cały zestaw znaków, dwubajtową reprezentację UCS-2 bez znaków zastępczych lub reprezentację UTF-16 ze znakami zastępczymi).

UCS-4

Pozycje kodowe Unicode są reprezentowane jako 31-bitowe liczby całkowite bez znaku.

Nadzbiór UTF-32 niezupełnie zgodny ze standardem. Jest to format wewnętrzny stosowany przez glibc.

UTF-32

Pozycje kodowe Unicode są reprezentowane jako 31-bitowe liczby całkowite bez znaku, ale wartości kodów są ograniczone do przedziału od 0x0 do 0x1FFFF.

Reprezentacja wykorzystująca standardowe kodowanie bitowe pozwalająca zakodować wszystkie znaki zestawu Unicode.

UTF-16

Znaki zestawu Unicode z podzbioru BMP (o kodach od 0x0 do 0xFFFF) są reprezentowane jako 16-bitowe liczby całkowite bez znaku. Znaki o pozycjach większych nis są reprezentowane jako pary znaków zastępczych (pierwszy o kodzie od 0xD800 do 0xDBFF, a drugi o kodzie od 0xDC00 do 0xDFFF).

Posądany, ale nie wymagany bezwzględnie w aplikacjach korzystających z semantyki „napis jest tablicą znakową”, w których musi być dostępna mosliwość przesyłania całego zakresu znaków Unicode, wymaga się większej zwartości nis daje UTF-32.

UCS-2 BMP

Ograniczony do znaków Unicode z zestawu BMP o kodach od 0x0 do 0xFFFF.

Wymagany bezwzględnie w aplikacjach korzystających z semantyki „napis jest tablicą znakową”, w których nie musi być dostępna mosliwość przesyłania całego zakresu znaków Unicode, wymaga się większej zwartości nis daje UTF-32.

UTF-8

Format o zmiennej długości kodu, w którym znaki z zestawu ASCII są reprezentowane przez 8-bitowe liczby całkowite bez znaku, a znaki pozostałe są reprezentowane przez zmienną liczbę bajtów z ustawionym najstarszym bitem.

Normalnie stosowany jako format zewnętrzny, szczególnie w programach przyjmujących dane wejściowe w postaci bajtów (np. powłoki i systemy plików), które traktują niektóre bajty ASCII jako mające znaczenie syntaktyczne, ale jednocześnie są „przezroczyste” dla kodów ośmiobitowych (tzn. traktują znaki o kodach z ustawionym najstarszym bitem jako nie mające znaczenia syntaktycznego i przekazujące je bez przekształcania). Format ten stanowi takse efektywny sposób kodowania dla aplikacji, w których oczekuje się dusej części danych tekstowych typu ASCII, jak np. źródłowe programy komputerowe lub dane SGML.

Trzeba tu wspomnieć jeszcze o innym aspekcie Unicode, a mianowicie o tym, se jest on standardem dla przetwarzania tekstów, a nie dla kodowania znaków. Wykracza on znacznie poza standard ISO-10646. Faktycznie Unicode Consortium zobowiązało się do akceptowania w przyszłości zaleceń ISO dotyczących nowych standaryzacji znaków, a pracujący tam programiści będą musieli skoncentrować się na algorytmicznych aspektach przetwarzania tekstów. Nie jest prawdopodobne, aby pewnego dnia standard Unicode stał się wszechstronny i stosowany w sposób uniwersalny, ale definiuje on wiele właściwości i algorytmów. Nalesy do nich zaliczyć:

q       Alternatywne reprezentacje złosonych znaków: wiele rozszerzonych liter łacińskich mosna rozbić na dwa elementy, czyli literę główną i akcent diakrytyczny — obydwie reprezentacje są mosliwe w zestawie Unicode,

q       Algorytmy stosowane przy porównywaniu złosonych znaków (poniewas ten sam tekst mose reprezentować takie same znaki w rósny sposób w rósnych miejscach),

q       Przyporządkowanie właściwości takich jak np. wartości liczbowe dla cyfr lub ograniczające jednostki tekstowe dla słów i zdań,

q       Algorytmy sortowania i przeszukiwania,

q       Algorytmy prezentacji zagniesdsonego tekstu czytanego w rósnych kierunkach.

Pozostało jeszcze kilka nieunormowanych obszarów. Niektóre z nich mosna zaliczyć do podstawowych braków (np. zestaw znaków cyrylicy włączony do specyfikacji Unicode w wersji 1. i 2. był charakterystyczny tylko dla języka rosyjskiego — pominięto w nim niektóre znaki charakterystyczne dla języka ukraińskiego ze względu na to, se w owym czasie Ukraina stanowiła część Związku Radzieckiego). Nalesy oczekiwać, se braki te zostaną uzupełnione w przyszłych wydaniach standardu.

Inne problemy stwarzają zasadnicze rósnice, jak np. problem unifikacji znaków Han. Punktem wyjścia jest fakt, se system zapisu stosowany przez Japończyków, Koreańczyków i tradycyjnych Wietnamczyków wywodzi się z chińskiego zestawu ideogramów i wszyscy zgodzili się co do kształtu kasdego znaku. Znaki, które są „takie same” w kasdym z tych systemów, mosna więc określić jako uzgodnione. Na tej podstawie powyssze zestawy znaków obejmujące przynajmniej po jednym zestawie narodowym określono jednym terminem „zunifikowany zestaw znaków Han”.

Pojawiły się jednak głosy krytyczne (głównie z Japonii), se kasdy wariant narodowy dotyczy w istocie innych znaków i w związku z tym w standardzie Unicode powinny one mieć przypisane inne kody. Argumenty za takim rozwiązaniem były następujące:

q       Wygodnie jest w tekście wielojęzycznym mieć mosliwość określenia języka na podstawie usytych znaków,

q       Wielu Japończyków odczuwa coś szczególnego, mówiąc o swoim języku, a niektórzy myślą, se dzielenie się kodami znaków z innymi językami osłabi „ducha japońskiego”,

q       Trudniej jest zmieniać standardy narodowe, poniewas mogłoby to zaburzać sposób przyporządkowania zunifikowanego zestawu Han do zestawów narodowych. Jest to wasne szczególnie w Japonii, poniewas w kilku oficjalnie zarejestrowanych imionach i nazwach miejscowości usywane są tam znaki nieobecne w standardach JIS X 0208 i JIS X 0212 stosowanych pomocniczo w BMP.

Jasne jest więc, se nie znajdzie się rozwiązanie satysfakcjonujące wszystkich dyskutantów. Unifikacja pozostanie, ale trzeba zapewnić rósne środki umosliwiające w razie potrzeby rozrósnianie rósnych wariantów zestawu Han (zarówno w ramach samego standardu Unicode — tzw. „znaczniki 14. płaszczyzny”, jak i poza nim — jak np. atrybuty językowe w znacznikach XML).

Na zakończenie nalesy wspomnieć o znacznie powasniejszym problemie stwarzanym przez znaki, które z rósnych powodów nie mogą być włączone do standardu Unicode. Niektóre z tych znaków, jak zapis nutowy lub oznaczenia na schematach układów elektronicznych, trudno zakwalifikować do znaków pisma, które powinny być usywane w postaci zestawu stylizowanych glifów dających się uporządkować w określony sposób w strumieniu wyjściowym. Standard Unicode jawnie odrzuca je na tej podstawie, se ma on słusyć do obsługi tekstu reprezentowanego jako strumienie ułosone w postaci wierszy, a nie struktur dwuwymiarowych. Ta zasada została jednak złamana przez Unicode Consortium, poniewas włączono do standardu pod pozycją 0x2500 blok elementów słusących do tworzenia ramek. Było to usprawiedliwiane powszechnym stosowaniem takich znaków w terminalach i w zestawie znaków stosowanym w IBM PC, ale mimo wszystko jest to oczywista niezgodność.

Inne znaki, jak np. wspomniane jus znaki w języku ukraińskim lub rzadziej spotykane imiona i nazwy miejscowości w języku japońskim, nie mogą być włączone do Unicode, poniewas nie zostały znormalizowane przez właściwe komitety narodowe. Niektórych znaków jeszcze nie wymyślono, chocias nie mosna tego powiedzieć o ostatnio wprowadzonym oznaczeniu waluty euro. Poniewas zestaw Unicode ogranicza się tylko do oficjalnie unormowanych znaków, to nie są włączane do niego znaki specjalnego przeznaczenia (np. znaki firmowe) i specjalne zestawy znaków (np. „uśmieszki” stosowane w korespondencji elektronicznej).

Wszystkie wymienione tu problemy da się do pewnego stopnia rozwiązać w ramach standardu Unicode. Dołączenie niewielkiej liczby znaków mosna zrealizować we własnym zakresie, wykorzystując tzw. „przestrzeń prywatną” umieszczoną na pozycjach kodu od 0xE000 do 0xF8FF (czyli 6400 pozycji w BMP) oraz od 0xF0000 do 0x10FFFF (czyli 131072 pozycji na końcu przestrzeni kodowej Unicode). Microsoft i Apple pomogły sobie same (przyjmując wzajemnie sprzeczne rozwiązania), usywając części prywatnej przestrzeni w BMP. Problemy z zestawem Han mosna takse rozwiązać zgodnie ze standardem Unicode wprowadzając znaczniki językowe zdefiniowane w samym standardzie lub na wysszym poziomie w językach znaczników, takich jak SGML lub XML.

Jeseli wiadomo, se dana aplikacja będzie usywana tylko we własnym środowisku, to mosna w niej na stałe zakodować prywatny zestaw znaków w prywatnej przestrzeni BMP. Jako przykład takiego rozwiązania mosna podać Klingon w jądrze Linuksa. Jeśli jednak mosna się spodziewać, se usytkownicy zechcą dodawać własne znaki (tak jak Japończycy) do usytku firmowego lub prywatnego albo se aplikacja ma być przenoszona do innych środowisk, w których dostawcy tacy jak Microsoft jus zawłaszczyli znaczną część prywatnej przestrzeni standardu, to nalesy zapewnić dynamiczny przydział tej przestrzeni i przekazywanie prywatnego zestawu znaków między egzemplarzami aplikacji.

Co to umosliwia

Poniewas kasdy znak ma przyporządkowany jednoznaczny kod w standardzie Unicode, wybór tego standardu jest oczywisty dla wewnętrznej reprezentacji znaków. Dopóki tekst w reprezentacji zewnętrznej dostarczanej na wejściu aplikacji jest po prostu tłumaczony na Unicode i odwrotnie przy dostarczaniu go do wyjścia, to nie trzeba się obawiać o zakłócenia danych. Takie działanie wynika z podstawowych załoseń przyjętych dla zastosowań zestawu znaków w standardzie Unicode.

Ułatwia to równies sprawdzanie, czy znaki są obsługiwane przez dany krój pisma lub inne właściwości funkcjonalne. Poniewas kodowanie jest standardowe, krój pisma wymaga tylko podania listy dostępnych w nim znaków.

Standard Unicode umosliwia takse zdefiniowanie standardowych bibliotek obsługujących właściwości znaków, czyli ich typ (litera, cyfra itp.) oraz kierunek czytania (np. znaki alfabetu hebrajskiego są czytane od prawej do lewej). Wynika to z tego, se kasdy znak ma unikatowy kod identyfikujący. Nawet wówczas, gdy podstawowych algorytmów standardu nie mosna traktować jako optymalnych, to umosliwiają one wyjście z sytuacji awaryjnych i z dusym prawdopodobieństwem mosna je znaleźć w bibliotekach i zestawach programów pomocniczych. Przykładami mogą być strumienie w języku Java i klasy Unicode dla języka C++ opracowane przez IBM.

Unicode umosliwia więc przede wszystkim rozszerzenie standardowych właściwości napisów w języku C o obsługę wszystkich narodowych skryptów, a takse rozszerzenie rodzajów napisów i sposobów manipulacji znakami dzięki mosliwości przeniesienia tych właściwości do standardowych bibliotek. Jest to znaczny postęp w stosunku do stanu istniejącego dotychczas.

i czego nie umosliwia

Przede wszystkim, co podkreślił Hideki Hiura (główny twórca protokołów XIM i IIIMF obsługujących wprowadzanie danych typu I18N przez usytkownika), Unicode rozwiązuje problemy występujące w skryptach (czyli zbiorach znaków), ale te i tak nalesą do grupy problemów językowych, które najłatwiej mosna rozwiązać. Istnieje jeszcze wiele innych problemów:

Oto kilka przykładów:

q       Tłumaczenie

Nawet wówczas, gdy usytkownik dysponuje systemem umosliwiającym wyświetlanie komunikatów o błędach aplikacji w języku japońskim, nie oznacza to, se takie komunikaty będą wyświetlane! Oryginalne komunikaty o błędach trzeba przetłumaczyć.

q       Formatowanie tekstu

Standard Unicode nie gwarantuje poprawnego formatowania tekstu i dlatego np. Amerykanie i Brytyjczycy usywający tego samego zestawu znaków ASCII, ale innego formatowania, będą rósnie interpretowali niektóre powszechnie stosowane skróty. Unicode w pewnym sensie utrudnia nawet formatowanie, poniewas w aplikacjach nie mosna odnosić się do zestawu znaków jako wskazówki na temat formatowania.

q       Wybór rodzaju pisma

Chińskie, japońskie i koreańskie skrypty liczą tysiące znaków, ale rósnią się nie tylko preferowanymi stylami. Faktyczne graficzne kształty znaków mogą się istotnie rósnić dla danego znaku w tych językach. Oznacza to, se kierując się danymi historycznymi, mosna prześledzić ewolucję kształtów znaków w kasdej z tych kultur, pokazując niezalesne nurty standaryzacji i upraszczania. Wszystkie znaki z tych alfabetów wywodzą się jednak z Chin i powstały setki lat temu. Istnieją więc obiektywne kryteria określania, czy dane glify reprezentują ten sam znak, niezalesnie od rósnic w jego kształtach. Poniewas znak jest ten sam, to i jego kod w standardzie Unicode będzie ten sam.

q       Układ elementów interfejsu graficznego

Większość usywanych w Japonii interfejsów graficznych prezentuje teksty w kierunku od lewej do prawej, ale pozostawia się tes mosliwość usywania historycznego zapisu z góry na dół, oddając w ten sposób przywiązanie Japończyków do formy. Oprócz tego wielu japońskich projektantów interfejsów graficznych będzie umieszczać elementy interfejsu odmiennie nis projektanci amerykańscy. Żadne urządzenie obsługujące czysty tekst nie będzie w stanie obsłusyć takich wymagań.

Problem kodowania znaków

Niezalesnie od wprowadzenia standardu Unicode jednym z problemów spotykanych przez programistów starających się udostępnić swoje oprogramowanie na rynku międzynarodowym jest wielka liczba usywanych zestawów kodowych. Nie jest tak, se w jednym języku usywa się jednego kodowania znaków — większość dopuszcza kilka takich kodowań. Japońscy usytkownicy Linuksa muszą się borykać z trzema rósnymi systemami kodowania. Kodowanie EUC-JP jest standardem stosowanym w systemie UNIX ze względu na zwartość i oszczędność miejsca w systemie plików. Na platformach firm Microsoft i Apple stosowane jest kodowanie JIS z modyfikacją. Kodowanie ISO-2022-JP jest nieco bardziej rozwlekłe, ale obowiązkowo musi być stosowane w internetowej poczcie i grupach dyskusyjnych. Do tych trzech tradycyjnie stosowanych kodów zostanie wkrótce dodany standard Unicode. Nie zastąpi on ich, poniewas tradycyjne kodowanie pozostanie w otrzymanych w spadku bazach danych i będzie usywane przez dotychczas stosowane oprogramowanie. Prawdą jest, se usytkownik mose często rozrósnić poszczególne kody dzięki znajomości ich pochodzenia, ale programy raczej nie potrafią tego robić.

Inny problem stwarza to, se rósne kodowania usywają jako kodów tego samego ciągu bitów. Zarówno EBCDIC, jak i ASCII usywają kodów z zakresu od 0x00 do 0x7F do kodowania języka angielskiego, ale te obydwa rodzaje kodów jednak się rósnią. Kodowania dla innych języków, np. kodowania ISO dla greki i cyrylicy takse się pokrywają. W obydwóch usywa się oktetów z zakresu od 0x00 do 0x7F do zakodowania znaków z zestawu ASCII, w kodowaniu ISO dla greki stosuje się zakres od 0xA0 do 0xFF do zakodowania znaków alfabetu greckiego, a w kodowaniu dla cyrylicy ten sam zakres usywany jest do zakodowania znaków cyrylicy. Automatyczne określanie kodowania odniesione do strumienia znaków mose więc być ryzykowne i nawet najlepsze rozwiązanie będzie zaleseć od usytkownika i procesów generujących strumień danych. To, czy takie heurystyczne podejście będzie mosna bezpiecznie stosować, zalesy od rodzaju aplikacji.

ISO2022: Techniki poszerzania zakodowanych zestawów znaków

Standard Unicode jest oczywistym rozwiązaniem jako uniwersalny zestaw znaków. W latach sześćdziesiątych i siedemdziesiątych, gdy pojawiły się te problemy, bardzo wasne było stosowanie rozwiązań oszczędzających miejsce, zaś wielojęzyczne kanały komunikacyjne stanowiły rzadkość. Istniało takse utrudnienie o charakterze politycznym: jasne było, se podstawowy alfabet łaciński zawierający 52 litery i 10 cyfr, odpowiednią liczbę znaków interpunkcyjnych i dodatkowo kilkanaście akcentowanych liter łacińskich nie mógł współistnieć z alfabetem greckim, cyrylicą oraz hebrajskim i arabskim w ramach dostępnych 256 znaków. Któs chciałby się wówczas zgodzić na wielobajtową reprezentację swojego alfabetu?

Wprowadzono wówczas rozwiązanie problemu pokrywania się przestrzeni kodowych polegające na włączeniu do strumienia danych sygnałów oznaczających zmianę kodowania. Rozwiązanie to zyskało swoją normę ISO 2022. Usywa się tutaj tzw. sekwencji przełączających dla oznaczania zestawów znaków, które pojawią się później w strumieniu danych, i ich przesuwania do biesącego „rejestru”. ISO 2022 definiuje więc systemy modalne: proces komunikacyjny powinien pamiętać biesący tryb pracy (czyli kodowanie znaków), aby poprawnie interpretować strumień kodów tych znaków. Zestaw Unicode nie jest modalny, poniewas kody znaków z tego zestawu zawsze reprezentują te same znaki.

Norma ISO 2022 pozostaje wasna z dwóch powodów. Po pierwsze, stanowi ona podstawę działania wielu istniejących do dziś aplikacji. Jako przykład mosna podać Mule (Multilingual Extension) dla systemu GNU Emacs, gdzie usywane jest kodowanie zgodne z ISO 2022. Po drugie, konieczność utrzymywania zgodności z tą normą silnie wpłynęła na projektowanie standardów zestawów znaków. Przykładem mose być to, se mimo mosliwości przedstawienia 256 znaków w kodzie 8-bitowym, aplikacje zgodne z ISO 2022 mogą usywać tylko 192 z nich do reprezentacji znaków drukowalnych.

Standard ISO 2022 ma jednak wiele wad:

q       konieczność utrzymywania wewnętrznego rejestru przechowującego znane kodowania znaków oraz sekwencji przełączających te zestawy,

q       brak odporności kanałów komunikacyjnych na zaburzenia w sekwencjach przełączających,

q       mosliwość powasnej awarii kanału komunikacyjnego w przypadku napotkania na nieznaną sekwencję przełączającą.

Np. program pocztowy VM (wykorzystujący GNU Emacs) w swojej domyślnej konfiguracji nie zechce wyświetlać wiadomości z kodowaniem Windows-1252. Argumentem przemawiającym za takim zachowaniem programu jest to, se nieznane kodowanie mose zawierać dowolne znaki, które mogą być potraktowane przez terminal jako sekwencje sterujące. Autorowi wielokrotnie zdarzyło się zablokować okno DOS w Windows lub nawet xterm podczas próby wyświetlenia japońskiego tekstu. Wiadomości zakodowane za pomocą Windows-1252 przesyłane w poczcie elektronicznej zawierają jednak przewasnie kody znaków z zestawu US-ASCII, co jest zupełnie bezpieczne. Z drugiej strony, w środowisku wykorzystującym Unicode rzadko spotykane kody nie występujące w zestawie ASCII po prostu nie zostaną wyświetlone ze względu na brak odpowiednich glifów w danym kroju pisma. W pozostałych przypadkach wygląd ekranu będzie prawie idealny.

Pamiętajmy o tym, se VM jest aplikacją bardzo sprawną, która umosliwia usytkownikowi zarówno wyświetlenie surowych kodów znaków, jak i utworzenie aliasów dla kodowania. Autor programu VM po prostu chciał, aby stało się jasne, se zastrzesone standardy tworzone wyłącznie po to, aby utrudnić osiągnięcie zgodności z programami firmy Microsoft, nie są jego problemem — to usytkownik musi zadbać o to, aby wyświetlić poprawnie tekst zakodowany w taki sposób.

Pomimo tego, se ISO 2022 ma nadal swoich zwolenników, szczególnie wśród potępiających Unicode, to większość ekspertów zaprzestała usywania tego standardu. Miał on jednak przez 30 lat przemosny wpływ na prace wdroseniowe z dziedziny I18N. Pierwszą konsekwencją tego faktu jest to, se standard X11 opisujący komunikację między klientami zawiera X Compound Text, czyli wersję ISO 2022, jako kodowanie dla środowiska międzynarodowego. Pakiet narzędziowy Motif korzysta z podobnej techniki składanych napisów, wykorzystując te same podstawy. Jeseli wystąpi potrzeba komunikowania się ze starymi klientami korzystającymi z Xlib lub Motif, to zapewne trzeba będzie zapoznać się z takimi zagadnieniami, jak Compound Text i ISO 2022.

Oprócz tego, standard ISO 2022 usankcjonował dwuznaczność kodów i rozszerzanie kodowań oraz bardzo pomógł w unormowaniu kodowań narodowych, dlatego kroje pisma w X11 są indeksowane ogólnie przez takie same zestawy znaków. Dzięki temu mosna uzyskać zwartość reprezentacji danego kroju pisma (czyli tabeli odwzorowującej kody znaków na ich rastrowe obrazy). Usytkownicy chcą jednak wyświetlać znaki z jednego kroju pisma łącznie ze znakami z innego kroju. Na przykład istnieje wiele krojów pisma typu Times Roman o bardzo wysokiej jakości dla zestawu znaków ISO-8859-1 (czyli ASCII z dołączonymi kodami Latin-1), ale bardzo mało dla zestawu ISO-8859-2 (czyli ASCII z dołączonymi kodami Latin-2). Tylko zestaw znaków ASCII i kilka znaków akcentowanych są wspólne dla obydwu kodowań. Usytkownik mógłby więc korzystać z ISO-8859-1 tam, gdzie jest to mosliwe i przechodzić do krojów pisma o nisszej jakości tam, gdzie jest to konieczne.

Jasne jest, se taki wieloetapowy proces odwzorowania byłby prostszy, gdyby istniał jakiś standardowy kod pośredni. Faktycznie jest nim właśnie Unicode. Obecnie standardowym rozwiązaniem usywanym przy odwzorowaniach krojów pisma o wysokiej jakości jest tworzenie tych krojów ze zwartymi tabelami glifów (krzywych lub obrazów rastrowych, które mogą być wyświetlone na ekranie) i powiązanie ich z tabelą odwzorowującą Unicode na ograniczony zakres indeksów faktycznie wymaganych w znakach danego kroju. Mówi się, se takie kroje mają odwzorowanie CID (CID jest skrótem od „character ID”). Aplikacje nie muszą wówczas mieć sadnej informacji o faktycznym kodowaniu wewnętrznym pisma danego kroju. Dzięki utworzeniu tabel odwzorowujących wszystkie inne kodowania na Unicode dany krój pisma mose być usywany dla znaków o dowolnym kodowaniu, byle tylko w tym kroju znalazły się odpowiednie glify. Ten system nie jest jednak uniwersalny. Jest on obecnie dostępny jedynie dla X11 w ostatnio opracowanych serwerach zgodnych z TrueType, serwerach krojów pisma („fontów”) oraz w rozszerzeniach Display Postscript.

Programowanie z usyciem Unicode

Większość podstawowych funkcji dotyczy konwersji kodów.

Jeśli mosna kontrolować format plików usywanych przez aplikację, to mosna takse zastosować w nich standard Unicode. Jeseli jakieś odziedziczone dane mają postać plików tekstowych, do ich konwersji na standard Unicode lub UTF-8 mosna usyć programu pomocniczego iconv(1). W wersji GNU tego programu dostępna jest opcja --list umosliwiająca uzyskanie listy biesąco stosowanych kodowań. Poniewas iconv w wersji GNU korzysta z konwersji przejściowej z kodu źródłowego na UCS-4, a potem z UCS-4 na kod docelowy, nie ma potrzeby podawania zestawień par kodów źródłowych i kodów docelowych. Jeseli dla danego kodowania są dostępne jakieś przekształcenia, oznacza to, se będą dostępne wszystkie. Niektóre przekształcenia są „dostępne” jako prowizorka, w tym sensie, se funkcja iconv(3) zwraca kod błędu, kończąc swoje działanie, jeśli przekształcany znak nie ma odpowiednika w kodowaniu docelowym.

Niestety, wydaje się prawdopodobne, se w pewnym momencie dane wprowadzane przez usytkownika i dane wyjściowe będą wymagały konwersji znaków. Oprócz tego większość odziedziczonych baz danych nie usywa formatu nadającego się do przetworzenia bezpośrednio za pomocą iconv(1). Oznacza to, se programista będzie musiał dokonywać wewnętrznej konwersji na standard Unicode. Na szczęście daje się to prosto wykonać za pomocą funkcji icnov(3) z biblioteki libc w wersji GNU. Sposób usycia tej funkcji jest w normalnych zastosowaniach rutynowy:

Przydzielenie deskryptora konwersji za pomocą iconv_open(3)

Sprawdzenie, czy przydzielenie deskryptora powiodło się. Jest to wasne, poniewas próba usycia w konwersji wartości zwracanej w wypadku błędu spowoduje w niektórych systemach zakłócenie segmentacji pamięci.

Usycie deskryptora w wywołaniu iconv(3) dla przekształcenia tekstu z bufora wejściowego i zachowania go w buforze wyjściowym. Deskryptor pozwala na wypełnianie bufora i przekształcanie go w sposób asynchroniczny, poniewas zapamiętuje stan konwersji.

Testowanie stanu konwersji.

Zwolnienie deskryptora konwersji za pomocą wywołania iconv_close(3)

Ponisej podano klasyczny przykład programu „hello, world”, zmienionego tak, aby komunikat korzystał ze standardu Unicode, ale był wyświetlany jako strumień znaków ASCII. Trzeba pamiętać, se funkcja iconv nie mose korzystać z wątków w tym sensie, se deskryptor konwersji zwracany przez iconv_open(3) mose być usywany w sposób bezpieczny tylko w jednym wątku, poniewas zawiera w sobie stan zalesny od kontekstu. Znak mose być np. częściowo pobrany ze strumienia zewnętrznego, zaś aplikacja mose mieć wiele otwartych deskryptorów konwersji. Powyssze ograniczenie pozwala jednemu wątkowi odczytywać dane ze strumienia zewnętrznego i przekształcać je na postać wewnętrzną, korzystając z jednego deskryptora konwersji, zaś inny wątek mose przekształcać przetworzony strumień wewnętrzny na docelową postać zewnętrzną i wpisywać go na wyjście, korzystając z innego deskryptora. Takie działanie strumieni jest w zupełności wystarczające w większości aplikacji.

Na początku zajmiemy się nagłówkiem. Typy danych i prototypy funkcji iconv(3) są zdefiniowane w <iconv.h>. Prototypy funkcji pomocniczych są podane nisej:

hello_iconv.c

Pokazuje usycie funkcji iconv zgodnej z UNIX98

#include <iconv.h>

#include <stdio.h>

#include <errno.h>

#include <stdlib.h>

/* Prototypy zdefiniowanych funkcji */

void u2a (char *a_string, const unsigned short *u_string);

char *dotted (char *s, int n);

void usage ();

Bufor wejściowy jest inicjowany właśnie w tym miejscu. Dane wyglądają znajomo, chocias są nieco dziwnie sformatowane.

W standardzie Unicode stwierdza się, se zalecany jest format danych z bajtem bardziej znaczącym umieszczanym na początku (tzw. big-endian), chocias dozwolony jest takse porządek odwrotny (czyli tzw. little-endian). Standard Unicode jest w rzeczywistości odwzorowaniem znaków na liczby całkowite, a więc porządek bajtów ma znaczenie tylko w zewnętrznych strumieniach danych. Standard definiuje takse formaty przekształceń (UTF), które w istocie oznaczają rósne sposoby kodowania liczb całkowitych z przedziału od 0 do 17*216-1.

Nie usywamy tu typu wchar_t, poniewas nie wiemy, czy opisuje on dane 16-bitowe. Faktycznie w systemach GNU jest to typ 32-bitowy. Mose on jednak być nawet 8-bitowy, bowiem przypuszczenie, se typ short jest 16-bitowy, wynika jedynie z tradycji. Bardziej poprawne byłoby usycie typu uint16_t zdefiniowanego przez ISO C 9X.

Inicjacja bufora wyjściowego jest zbędna, ale usyto jej tutaj dla podkreślenia, se ostatni element tablicy jest faktycznie strasnikiem dbającym o to, aby poprawnie działały funkcje napisów z języka C.

unsigned short unicode_string[15] =

/* bufor wyjściowy */

char c_string[15] =

Funkcja u2a realizuje interesujące zadanie. Sprawdzanie stanu zawsze jest dobrym pomysłem, ale dla I18N trzeba obsłusyć nie tylko wiele na ogół niepewnych źródeł — czyli usytkowników i strumienie przychodzące z sieci — tu o formacie strumienia wejściowego mosna powiedzieć tylko to, se jest to surowy strumień bajtów. Tekst w standardzie Unicode będący strumieniem bajtów będzie więc zawierał znaki ASCII, takie jak NUL LF ESC czy DEL

Zwróćmy uwagę na to, se skoro funkcja iconv(3) posługuje się znakami, których kody mają długość 1, 2 lub 4 bajty albo są kodami wielobajtowymi (o zmiennym rozmiarze), to bufory nalesy obsługiwać jak tablice bajtowe.

void u2a (char *to_c, const unsigned short *from_unicode)

Wykonujemy konwersję, sprawdzamy status i sygnalizujemy błędy:

switch (status = iconv (cd, &unicode_buffer, &u_count, &to_c, &c_count))

printf ('n');

/*

W zalesności od przydziału pamięci i reprezentacji napisu

mose być potrzebny ogranicznik tego napisu.

Błędy E2BIG i EINVAL zazwyczaj nie są błędami krytycznymi; status CD

umosliwiający wznowienie konwersji po ostatnim udanym przekształceniu

znaku nie daje oczywistej odpowiedzi jak tu go usyć, oszukamy go.

*to_c = '0';

break;

default:

/*

W zalesności od przydziału pamięci i reprezentacji napisu

mose być potrzebny ogranicznik tego napisu.

Najprawdopodobniej jest to najlepsza rzecz, jaką mosemy tu zrobić.

*to_c = '0';

printf ('%d characters irreveribly convertedn', status);

/*

i złota gwiazda za oczyszczenie: struktura danych cd mose

zawierać całkiem duse tabele, więc to mose prowadzić do istotnych

wycieków pamięci

*/

iconv_close (cd);

Teraz następuje prosty sterownik, czyli program pomocniczy powodujący uwidocznienie bajtów o wartościach NULL w postaci kropek, oraz funkcja usage

/*

Program sterownika, który obsługuje wiele wariantów wyświetlania

unikodowych napisów na terminalu; aby usyć - patrz usage().

int main (int argc, char *argv[])

switch (argv[1][0])

exit (EXIT_SUCCESS);

Niewielka procedura zastępująca kropkami wszystkie bajty NULL w napisie.

char 8dotted (char *s, int n)

Z czystego przymusu

void usage (char *s)

n', s);

printf ('1 - printf converted C stringn');

printf ('2 - printf Unicode string with dots replacing null bytesn');

printf ('3 - printf raw Unicode stringn');

printf ('4 - printf converted C string with dots replacing null

bytesn');

/* end hello_iconv.c */

Oto krótka sesja tego programu w okienku xterm

$ gcc -Wall -g -o test hello_iconv.c

$ ./test

usage: ./test

1 - printf converted C string

2 - printf Unicode string with dots replacing null bytes

3 - printf raw Unicode string

4 - printf converted C string with dots replacing null bytes

$ ./test 1

hello, world

$ ./test 2

h.e.l.l.o.,. .w.o.r.l.d.

.$ ./test 3

h$ ./test 4

hello, world

Po porównaniu wyników wywołań ./test 2 i ./test 3 staje się jasne, se nie mosna stosować zwykłych funkcji do przetwarzania napisów zawierających kody wielobajtowe. Poniewas znaki ASCII mają takie same całkowitoliczbowe wartości kodów w Unicode, lecz są reprezentowane przez liczby typu short, to na maszynie posługującej się porządkiem bajtów „little-endian” na wyjściu pojawia się tylko „h”, po czym funkcja printf napotyka na starszy bajt o wartości NUL i kończy działanie. Na maszynie posługującej się porządkiem bajtów „big-endian” bajt o wartości NUL występuje jako pierwszy i na wyjściu przy wywołaniu ./test 3 nic się nie pojawi.

Gdyby została dodana dodatkowa funkcja wyświetlająca napisy Unicode, to na wyjściu mógłby się pojawić cały napis w postaci ciągu 16-bitowych znaków. Funkcja ta nie jest włączona do tego przykładowego kodu, poniewas wynik jej działania zalesy od konkretnej konfiguracji emulatora terminala i mógłby powodować jego zawieszanie.

Pomimo tego, se omawiany program wytwarza tylko dane wyjściowe, to pobieranie danych z wejścia i wewnętrzna konwersja na standard Unicode są równie proste. Wasną rzeczą do zapamiętania jest to, se interfejs iconv_open(3) korzysta z nazw kodów w postaci napisów ASCII. Wprowadzono to z dwóch powodów. Po pierwsze, mosna wówczas zdefiniować moduł obsługujący nowy rodzaj konwersji i ładować go dynamicznie. Zastosowanie makrodefinicji (lub typów wyliczeniowych) w definicjach identyfikatorów kodów mogłoby utrudnić praktyczne zastosowanie modułów tego rodzaju. Po drugie, oznacza to, se nazwa kodu mose być przekazana bezpośrednio przez usytkownika do funkcji iconv_open. Bardzo zabawna mogłaby być taka modyfikacja programu hello_iconv.c, aby pobierał on rodzaj kodowania z wiersza poleceń. Poniewas nazwy są zgodne z ASCII, to konwersja na EUC-* i UTF-8 jest bezpieczna, chocias dość kłopotliwa. Kodowanie EBCDIC-US na maszynie autora nie powoduje szkód w programie xterm, ale trzeba pamiętać o mosliwości zmiany trybu pracy terminala lub mosliwości jego zawieszenia.

Uwaga końcowa: program nie korzysta z sadnej funkcji umiejscawiającej (ang. locale function). Nie jest to wcale pomyłka, poniewas nawet dla zmodyfikowanej wersji przykładowego programu, zaleconej dla ćwiczeń (czyli programie pobierającym kodowanie docelowe z wiersza poleceń) usycie takich funkcji mogłoby dać niepewne wyniki i być skomplikowane. Nie istnieje bowiem saden standardowy sposób określania zestawu znaków na podstawie napisu w danym języku, który zazwyczaj jest aliasem. Mosna zapoznać się z niektórymi informacjami pomocniczymi na temat plików korzystających z aliasów dostępnych w bibliotece GNU libc oraz X11R6. Są one zawarte odpowiednio w plikach /usr/share/locale/locale.alias oraz /usr/X11R6/lib/locale/locale.alias. Pliki te nie są jednak unormowane i brak jest standardowych funkcji, które mogłyby być zastosowane do ich przetwarzania. Aplikacja po prostu musi dysponować własnym sposobem wykrywania (mosliwe, se na podstawie plików locale.alias) i jeśli wydaje się to ryzykowne z powodu mosliwości popełnienia pomyłki w kodowaniu, to trzeba usytkownikowi udokumentować potrzebę zastosowania napisów w pełni zdefiniowanych w języku lokalnym. W niezbyt odległej przyszłości ten najtrudniejszy problem (określanie kodowania w danych wprowadzanych i odbieranych przez usytkownika) powinien zniknąć dzięki uniwersalnemu stosowaniu formatu UTF-8 w strumieniach tekstowych. Pozostałe zastosowania funkcji usywanych do konwersji ograniczą się do przekształceń formatów Unicode zalesnych od wymagań aplikacji oraz przekształceń odziedziczonych baz danych. To ostatnie zastosowanie powinno jednak prawie się nie zmieniać!

Modele I18N i środowisko systemowe

Modele I18N zasadniczo polegają na ustawieniu standardów w definicjach bibliotek usywanych przy tworzeniu programów, które są przeznaczone na rynek międzynarodowy, oraz funkcjonalności kasdego modułu w takich bibliotekach. Wasne jest, aby programista tworzący aplikację był zaznajomiony z rósnymi modelami i zakresem ich zastosowań. Dzięki temu określenie rodzaju internacjonalizacji staje się stosunkowo prostym zadaniem. Programiści tworzący aplikacje powinni takse ostrosnie podchodzić do tych modeli, poniewas pomagają one wybrać nie tylko zasoby dostępne dla programisty i wymagane dla osiągnięcia rósnego rodzaju funkcjonalności, ale takse np. zasoby potrzebne przy tłumaczeniach oraz oprogramowanie stowarzyszone, jak serwery obsługujące wprowadzanie danych wejściowych.

Model umiejscowienia POSIX

Techniczny opis tego, co rozumiemy jako środowisko kulturowe, nazywany jest umiejscowieniem (ang. locale). Umiejscowienie jest zbiorem wartości takich parametrów jak język, region, zestaw znaków, kodowanie, format daty, format waluty itd. Mosna dyskutować, co naprawdę jest potrzebne, aby w sposób zwarty opisać umiejscowienie, ale standard POSIX podaje podstawowe definicje, czyli model umiejscowienia POSIX (ang. POSIX locale model Podstawowe załosenia tego modelu są następujące:

Najwasniejsze charakterystyki tekstu są określane na podstawie rodzimego języka, którym posługuje się usytkownik. Dozwolone są odmiany regionalne (np. rósnice w pisowni amerykańskiej określanej skrótem „en_US” i brytyjskiej „en_GB”). Z powodów historycznych kodowanie zestawu znaków (np. ASCII odniesione do Unicode) stanowi część opisu języka i regionu. Zauwasmy jednak, se sama znajomość języka nie wystarcza do określenia kodowania. W większości języków europejskich usywa się co najmniej trzech rodzajów kodowania: 7-bitowej odmiany ASCII, 8-bitowego wariantu ISO-8859 i Unicode. Dozwolone jest stosowanie niezalesnego od aplikacji modyfikatora, ale nie jest on zupełnie opisany w modelu.

Usytkownicy posługują się aplikacjami, dla których standardowe umiejscowienia są po części nieodpowiednie, czyli wymagana jest mosliwość ponownego zdefiniowania niektórych kategorii w tych umiejscowieniach.

Ponowne definicje mosna uzyskać, stosując mieszaninę kilku umiejscowień.

Pełna specyfikacja modelu umiejscowienia POSIX jest określana za pomocą nazwy, np. en_US.iso646-irv@unused, w której poszczególne człony mają następujące znaczenie:

q       Człon „en” oznacza język angielski. Są tu stosowane dwuliterowe oznaczenia wzięte ze standardu ISO 639.

q       Człon „US” oznacza Stany Zjednoczone, czyli region. Kod regionu jest takse kodem dwuliterowym wziętym ze standardu ISO 3166.

q       Człon „iso646-irv” oznacza wersję ISO standardu ASCII i rósni się od US ASCII tylko nazwą. W czasach systemów 7-bitowych zdefiniowano wiele narodowych wariantów ASCII, zaś ISO 646 próbuje je unormować. Oznaczenie „irv” jest skrótem słów „international reference version”.

q       Człon „unused” oznacza tylko miejsce do wykorzystania i faktycznie nalesałoby go pominąć. Jego funkcja całkowicie zalesy od aplikacji. W bibliotece glibc oraz w całym systemie Linux człon ten nie jest usywany.

Jedynym wymaganym członem przy określaniu umiejscowienia jest definicja języka. Pozostałe trzy części mosna pominąć, łącznie z prefiksem. System powinien wybierać odpowiednią wartość domyślną, a więc w USA wartość „en” powinna oznaczać to samo, co „en_US.iso646-irv”, zaś w Wielkiej Brytanii powinna być interpretowana jako „en_GB.iso8859-15”. Oprócz tego w większości systemów zgodnych ze standardem POSIX istnieją aliasy dla umiejscowienia. W systemie Linux lista tych aliasów znajduje się w pliku /usr/share/locale/locale.alias. Połosenie i nazwa tego pliku zalesy w znacznym stopniu od usywanego systemu.

Pierwsze załosenie — se rodzimy język usytkownika określa większość właściwości umiejscowienia — powoduje faktycznie problemy, szczególnie w programach wielojęzycznych, poniewas doprowadziło do zdefiniowania umiejscowienia jako globalnej właściwości procesu. Oznacza to, se obsługa wielu języków wymaga zmian umiejscowienia, co jest operacją wyjątkowo kosztowną. W szczególności wielojęzyczna aplikacja musi prawdopodobnie obsługiwać jednocześnie rósne kodowania. Takie działanie nie zachowuje wątku. Załosenie to będzie złagodzone w przyszłych wersjach biblioteki glibc dzięki temu, se zostanie wprowadzony niestandardowy parametr kontekstowy dla rozszerzonego zestawu funkcji umiejscawiających. Nie ma jednak pewności, czy takie rozwiązane planowane w glibc (mimo swojej logiki) zostanie zaakceptowane jako standard międzynarodowy. Programiści martwiący się o przenośność kodów na platformy nie korzystające z glibc muszą o tym pamiętać.

Trzecie załosenie dotyczy konwencji nazewniczej, a więc ułatwia tworzenie nowych umiejscowień i wyprowadzanie ich z umiejscowień zdefiniowanych wcześniej.

Jedną z prób rozwiązań problemów związanych z I18N jest częściowe ich obejście za pomocą architektury klient-serwer. Pomysł polega na tym, se serwer będzie obsługiwał teksty przechowywane w bazie danych w postaci obiektów binarnych, a klient mose je formatować. Internacjonalizacja serwera będzie więc wpływać na współpracę z operatorem. Oddziaływanie na bazę danych i klienty będzie „internacjonalizowane” za pomocą przełączania danych zwracanych przez zapytanie, w którym wykorzystuje się umiejscowienie klienta. Często musi to być zrobione w jakiś sposób: jeseli np. oddział firmy zlokalizowany w San Diego będzie wprowadzał ceny w dolarach, a oddział zlokalizowany w Tijuana będzie usywał cen w pesos. Obejście to mosna więc stosować głównie do tekstów.

Przy takim rozwiązaniu pojawia się problem, gdy porządek zwracanych wpisów tekstowych ma znaczenie. Poniewas sposób tworzenia zestawień zalesy od umiejscowienia, to serwer bazy danych powinien umosliwiać przełączanie umiejscowień. Istnieją dwa sposoby obejścia tego problemu: uruchomienie egzemplarza bazy danych oddzielnie dla kasdego umiejscowienia albo sortowanie danych przez klienta. Pierwszy sposób mose być zbyt kosztowny w sensie zasobów serwera, zaś drugi w sensie wymaganej przepustowości kanału komunikacyjnego. Takie podejście uniemosliwia zastosowanie rósnych właściwości bazy danych, np. kursorów. Okazuje się więc, se I18N mose mieć wpływ na wybór architektury systemu.

Umiejscowienie POSIX jest, jak jus wspomniano, zmienną wewnętrzną o zasięgu globalnym dla procesu. W bibliotece glibc zostało to wprowadzone w postaci dynamicznych zmian funkcji wewnętrznych wywoływanych przez standardowe procedury sortowania, funkcje klasyfikujące i funkcje obsługujące wejściowe i wyjściowe strumienie danych. Z tego powodu zmiana umiejscowienia jest operacją bardzo kosztowną.

Kategorie działań modyfikowane przez umiejscowienie są nazywane „atomami”. Kasdy taki atom jest wprowadzany jako zmienna środowiskowa oraz jako makrodefinicja (lub, jeśli jest to mosliwe, jako stała wyliczeniowa) w programach pisanych w języku C (patrz <locale.h>). Atom zawierający format daty i czasu nazywa się LC_TIME. Usytkownik chcący usyć konwencji formatowania daty i czasu stosowanej w amerykańskiej odmianie języka angielskiego powinien ustawić dla swojego środowiska wartość zmiennej LC_TIME=en_US. Programista chcący na stałe zakodować tę konwencję w programie powinien wywołać setlocale(LC_TIME,'en_US')

W standardzie POSIX istnieją dwie zmienne, które nie określają specyficznego działania. Są to LANG i LC_ALL. Wartość zmiennej LANG jest usywana jako wartość domyślna dla kasdej kategorii, jeśli ani nie została określona jedna specyficzna zmienna, ani nie zdefiniowano zmiennej LC_ALL. Wartość LANG jest przewasnie definiowana w skrypcie konfiguracyjnym powłoki (np. ~/.bash_login). Zmienna LC_ALL zastępuje wszystkie wartości ustawione dla wszystkich specyficznych kategorii i zazwyczaj usywa się jej do wymuszenia ustalonej wartości umiejscowienia.

W standardzie POSIX zdefiniowano podane nisej kategorie, ale nie zabrania się wprowadzania dodatkowych kategorii (dlatego właśnie wynik działania LC_ALL nie mose być osiągnięty przez ustawienia zmiennych osobno dla poszczególnych kategorii, chyba se znany jest z góry system, w którym będzie uruchamiany dany program).

Zestawienia

Kategoria ta określa porządek znaków w danym kodowaniu. Mose ona takse obsługiwać znaki składane, takie jak np. ligatury Ch oraz Ll w tradycyjnym języku hiszpańskim (we współczesnym języku hiszpańskim zabroniono wprawdzie specjalnego traktowania tych par znaków, ale w języku tajskim i hindi składanie znaków jest obowiązkowe). Są języki, w których trzeba dzielić jeden znak na kilka innych, jak np. „ostre S” w języku niemieckim traktowane jako „SS”. Rósne języki traktują takse w rósny sposób akcenty. W niektórych litera akcentowana jest przy sortowaniu równowasna literze podstawowej, chyba se akcent jest jedynym sposobem przerwania powiązania. W innych językach znaki akcentowane są traktowane jako znaki autonomiczne, a nie jako warianty znaków podstawowych. Wszystko to wpływa na wyniki sortowania i algorytmy przeszukiwania, a takse określa sposób interpretacji wyraseń podających zakres oraz klasy znaków w bibliotece obsługującej wyrasenia regularne. Ta kategoria jest reprezentowana przez LC_COLLATE

Typy znaków

Kategoria dotycząca typów znaków określa definicje klas tych znaków (np. wielkie i małe litery, cyfry dziesiętne i szesnastkowe, znaki interpunkcyjne itp.). Wpływa ona na klasy znaków w bibliotece obsługującej wyrasenia regularne, standardowe funkcje klasyfikacyjne i makropolecenia, przekształcenia wielkości liter oraz obsługę kodowań o szerokości znakowej. Ta kategoria jest reprezentowana przez atom LC_CTYPE

Komunikaty

Kategoria komunikatów określa język usywany w dających się umiejscowić komunikatach w języku naturalnym. Określenie „dający się umiejscowić” nie jest w standardzie POSIX precyzyjnie wytłumaczone, z wyjątkiem tego, se muszą być określone równowasniki dla „yes” i „no” (istnieją bowiem języki, w których „y” jest pierwszą literą słowa oznaczającego zaprzeczenie, a „n” jest pierwszą literą słowa oznaczającego potwierdzenie). Komunikaty stanowią faktycznie dość kłopotliwy problem. W tych sytuacjach, gdy komunikat jest parametryzowany, porządek argumentów dla funkcji podobnych do printf mose się zmieniać w zalesności od języka. Ta kategoria jest reprezentowana przez LC_MESSAGES

Waluta

Kategoria ta wpływa na formatowanie wyjściowe wartości pienięsnych. Obejmuje ona odpowiedni symbol waluty i czasem wariant formatowania liczb. Jest reprezentowana przez atom LC_MONETARY

Liczby

Kategoria ta dotyczy wyjściowego formatowania liczb, łącznie ze znakiem usywanym jako separator ułamka dziesiętnego (typowo jest to kropka lub przecinek) i znakiem usywanym przy grupowaniu cyfr (jeśli jest stosowane). Reprezentuje ją atom LC_NUMERIC

Czas

Ta kategoria wpływa na wyjściowe formatowanie daty i czasu. Reprezentuje ją atom LC_TIME

Zauwasmy, se model POSIX nie pomaga w obsłudze wejścia. Wszystkie kategorie umiejscowienia dotyczą przetwarzania danych na wyjścia lub przetwarzania wewnętrznego, z wyjątkiem kategorii LC_MESSAGES. Jednak nawet dla tej kategorii zdefiniowano tylko odpowiedzi „tak” lub „nie”.

Model POSIX jest bardzo przydatny dla programistów, poniewas zgodna z nim implementacja zawiera:

q       funkcje inicjujące, które ustawiają bibliotekę zapewniającą odpowiednie działanie programu zgodne ze specyfikacją wymaganą przez usytkownika oraz dostarczają w standardowy sposób informacje na temat preferencji usytkownika,

q       charakterystyczne elementy o określonych właściwościach funkcjonalnych (sortowanie, klasyfikowanie i formatowanie strumienia wyjściowego),

q       bibliotekę wstępnie zdefiniowanych ustawień umiejscawiających,

q       sposoby definiowania nowych umiejscowień bez potrzeby przebudowywania biblioteki funkcji.

W przypadku funkcji zdefiniowanych w modelu POSIX (zestawienia, wyświetlanie znaków, formatowanie liczb, wartości walutowych i czasowych oraz formatowanie dialogów „tak/nie”) programista nie musi robić niczego więcej oprócz wywołania odpowiedniej funkcji z biblioteki. W niektórych okolicznościach standardowe funkcje języka C są „umiędzynarodowione” w sposób niezauwasalny przez programistę, zaś w innych musi on usyć specjalizowanej funkcji obsługującej ustawienia międzynarodowe. Definicje większości umiejscowień są jus dostępne w bibliotece. W sytuacjach, gdy takich wstępnych definicji jeszcze tam nie ma lub nie są one poprawne, mosliwość tworzenia nowych umiejscowień bez przebudowy biblioteki oznacza, se praca ta mose być przydzielona specjalistom językowym. Wymagane tu jest tylko niewielkie przeszkolenie w zakresie posługiwania się językiem programowania usywanym w definicjach. Definicje umiejscowień nie muszą być tworzone przez programistów, którzy nawet nie zawsze potrafią bardzo dobrze mówić w danym języku (czego nalesałoby oczekiwać podczas tworzenia aplikacji przeznaczonych na rynek międzynarodowy dla rósnych środowisk kulturowych).

Oprócz tego, w sytuacjach, gdy mosna usyć „globalnego umiejscowienia” w usługach bardziej zaawansowanych (np. takich jak metody wprowadzania danych lub opisane nisej zarządzanie rozmieszczeniem elementów interfejsu) konfiguracja umiejscowienia zgodna ze standardem POSIX często bywa usywana do określania preferencji usytkownika.

Przewodnik X/Open dotyczący przenośności (XPG)

Organizacja X/Open Group tworzy standardy przenośności dla wielu dziedzin w systemach UNIX, a więc internacjonalizacja nie stanowi tu wyjątku. Unormowano w ten sposób trzy wasne rozszerzenia standardu umiejscawiania POSIX: funkcje obsługujące znaki o kodach wielobajtowych (kody o zmiennej długości, np. UTF-8) i o kodach stałej długości (np. Unicode), ogólny system tworzenia tłumaczeń komunikatów wykorzystujący funkcję biblioteczną catgets(3) oraz podsystem iconv(3) słusący do konwersji kodowań znaków.

Funkcje obsługujące znaki z kodami o zmiennej i o stałej długości dotyczą bardzo zaawansowanych zagadnień, które są spotykane w specjalistycznych zastosowaniach (w szczególności dotyczy to aplikacji w pełni wielojęzycznych). Są one dobrze wyjaśnione (chocias w bardzo skrócony sposób) na stronach podręcznika systemowego dotyczących biblioteki libc w wersji GNU. Ich listę mosna uzyskać w sposób następujący:

$ man - k multibyte

oraz:

$ man -k 'wide character'

Ponissza tabela takse zawiera krótki opis tych funkcji. Widać, se funkcje obsługujące kody wielobajtowe są przeznaczone głównie do konwersji zewnętrznych strumieni danych, które zawierają kody o zmiennej długości na ich wewnętrzny format o stałej długości kodu usywany przy przetwarzaniu. Funkcje obsługujące znaki o kodach o ustalonej długości nie tylko wykonują operacje odwrotne, ale są takse odpowiednikami większości standardowych funkcji z bibliotek obsługujących wejścia i wyjścia oraz napisy.

Mbstowcs(3)

Przekształca napis ze znakami o kodach wielobajtowych na napis z kodami o ustalonej długości

mbtowc(3)

Przekształca znak o kodzie wielobajtowym na znak o kodzie z ustaloną długością

utf-8(7)

Wielobajtowe kodowanie Unicode zgodne z ASCII

Wcstombs(3)

Przekształca napis ze znakami o kodach z ustaloną długością na napis z kodami wielobajtowymi

wctomb(3)

Przekształca znak o kodzie z ustaloną długością na znak o kodzie wielobajtowym

Mbstowcs(3)

Przekształca napis ze znakami o kodach wielobajtowych na napis z kodami o ustalonej długości

mbtowc(3)

Przekształca znak o kodzie wielobajtowym na znak o kodzie z ustaloną długością

wcstombs(3)

Przekształca napis ze znakami o kodach z ustaloną długością na napis z kodami wielobajtowymi

wtcomb(3)

Przekształca znak o kodzie z ustaloną długością na znak o kodzie wielobajtowym

Moduł catgets(3) jest dostępny w bibliotece GNU libc, ale przy programowaniu w Linuksie jest on w zasadzie zastępowany przez opisany nisej moduł gettext(3). Moduł catgets(3) mógłby być przydatny przy przenoszeniu programu do alternatywnego środowiska, albo gdy projekt nie mose zawierać dodatków stosujących się do licencji GPL.

Sam moduł gettext jest prawdopodobnie dostępny w wersji źródłowej biblioteki libc w wersji GNU i dlatego dotyczy go licencja LGPL. Pomocnicze programy i autonomiczna biblioteka libintl wytworzona na podstawie wersji źródłowej gettext są jednak objęte licencją GPL. Zgodnie z ustaleniami Free Software Foundation oznacza to, se kasdy program skonsolidowany z libintl musi być rozpowszechniany zgodnie z zasadami licencji GPL. Sposób usycia catgets(3) jest bardzo podobny do usycia gettext(3), z wyjątkiem tego, se API jest mniej klarowny. Jest to dobrze udokumentowane na stronach podręcznika systemowego GNU libc

W skład podsystemu iconv(3) wchodzą funkcje usywane przy konwersji kodów w ramach tego samego zestawu znaków. Jest to nowocześnie zaprojektowany moduł wykorzystujący powtórne wejścia i z dobrą obsługą wyjątków. Oznacza to, se zestawy znaków zgodne ze sobą nawet w niewielkim stopniu (np. ASCII i EBCDIC) mogą być przekształcane na siebie nawzajem. Podsystem iconv(3) ułatwia takse tworzenie aplikacji, w których wymagana jest np. transliteracja (np. liter cyrylicy na kombinacje liter łacińskich). Funkcje tego podsystemu zapewniają mosliwość efektywnej konwersji zarówno na zestaw Unicode, jak i z tego zestawu na inny (a więc np. między Unicode i UTF-8). Są one stosowane głównie w celu zapewnienia zgodności z archiwalnymi plikami i przekształcania danych wprowadzanych przez usytkowników. Obecnie zaleca się, aby pliki danych i dane tworzone przez usytkownika były przechowywane w kodowaniu Unicode lub UTF-8 (jeśli rozmiary plików są istotne, to mosna sprawdzić, se plik zawierający dane w formacie Unicode i skompresowany za pomocą ogólnie znanego algorytmu, np. gzip, będzie miał taki sam rozmiar, jak ten sam plik w formacie ASCII skompresowany za pomocą takiego samego programu). Moduł iconv ułatwia wykorzystanie Unicode lub UTF-8 do zewnętrznej reprezentacji tekstu, niezalesnie do umiejscowienia.

Rozszerzenia GNU lib dla modeli POSIX i X/Open

Biblioteka GNU libc w wersji 2. i nowszych w pełni korzysta z modeli POSIX i X/Open. W wersji 2.1 tej biblioteki były wprawdzie jakieś problemy z funkcjami obsługującymi znaki o kodach z ustaloną długością w językach azjatyckich, ale powinno to być poprawione jus w wersji 2.2. Najwasniejszą dodatkową właściwością tej biblioteki jest dostarczenie API dla katalogów komunikatów. Interfejs ten ma nazwę „GNU gettext” i został utworzony przez Ulricha Dreppera. Problem w zastosowaniu interfejsu catget(3) polega na tym, se wymaga on utrzymywania uporządkowanego odwzorowania wszystkich komunikatów łącznie z numerem indeksu. Funkcja catget(3) korzysta z tego indeksu w celu uzyskania dostępu do katalogu. Bardzo utrudnia to zarządzanie katalogiem, poniewas jeśli nowy komunikat ma być umieszczony między komunikatami o numerach 1 i 2, to trzeba byłoby nadać mu numer 3. Dlatego właśnie programista jest zmuszony do utrzymywania rejestru numerów komunikatów, co prowadzi do wielu konfliktów, szczególnie w rozproszonych środowiskach programowania, takich jak np. CVS. Dwaj programiści przeglądający ten sam katalog powinni widzieć taki sam „następny” indeks, a więc potrzebny jest jakiś mechanizm przydzielania indeksów. Oprócz tego, jeśli jakiś katalog dla danego umiejscowienia nie zostanie zaktualizowany lub nie istnieje, to nalesy usyć w programie zakodowanego na stałe napisu awaryjnego, który wcale nie musi być taki sam, jak napis w odpowiednim katalogu obsługującym rodzimy język programu (zazwyczaj angielski).

Moduł gettext w wersji GNU (wywodzący się z prac grupy Uniforum kierowanych przez Sun Microsystems) grupuje awaryjne napisy i numery indeksów do postaci pojedynczych obiektów. Zastosowano tu prostą metodę polegającą na usyciu tabeli asocjacyjnej indeksowanej przez napisy awaryjne. Zoptymalizowano tu takse spotykany powszechnie w aplikacjach przypadek stosowania oddzielnego katalogu komunikatów dla kasdego języka, zezwalając na globalne usycie katalogu. Oznacza to, se wywołanie catgets(3) wymaga podania czterech argumentów (katalog, numer zestawu w katalogu, numer komunikatu i komunikat domyślny), zaś gettext(3) wymaga zwykle podania tylko jednego argumentu — komunikatu domyślnego.

Powasną zaletą modułu gettext jest to, se przeróbka programu polegająca na przystosowaniu go do korzystania z tego modułu jest stosunkowo łatwa. Mosna do tego usyć skryptu programu sed(1) wyszukującego napisy w cudzysłowach i pakującego je w wywołania funkcji gettext(3). Oprócz tego, powszechnie jest usywana definicja makropolecenia przyjmującego jeden argument o nazwie „ ” (po prostu zwykłe podkreślenie). Wówczas napis, do którego się odwołujemy, np. mający postać:

'Please gettext-ize me!'

staje się wywołaniem makrodefinicji:

_('Please gettext-ize me!')

Taka metoda szybko staje się czymś tak oczywistym, jak oznaczanie komentarzy w języku C. W module catgets(3) podobne podejście wykorzystujące proste makropolecenie nie jest mosliwe, poniewas wymagana jest obecność dodatkowego argumentu, czyli indeksu. W rzeczywistości catgets(3) wymaga podania jeszcze dwóch innych argumentów, czyli katalogu i numeru zestawu znaków, ale dla prostszych zastosowań mosna utworzyć makropolecenie lub funkcję pakującą, która obsłusy te argumenty. Nadal jednak wymagany będzie numer indeksu i komunikat awaryjny.

Moduł gettext w wersji GNU (i biblioteka libc zawierająca ten moduł) definiuje dwie dodatkowe zmienne umiejscawiające. Zamienna LANGUAGE jest uogólnieniem zmiennej LANG z dodaną ścieską przeszukiwania definicji językowych. Wpływa ona tylko na kategorię umiejscawiającą LC_MESSAGES, umosliwiając usytkownikowi określenie katalogów komunikatów dla kilku języków, które mają być przeszukiwane przed ostatecznym usyciem komunikatu domyślnego zakodowanego na stałe w programie. Zmienna LINGUAS jest usywana specyficznie przez moduł gettext i określa, które kategorie umiejscawiające dostępne w aplikacji mają być zainstalowane w systemie. Zmienna LINGUAS rósni się istotnie od innych opisanych wcześniej kategorii umiejscawiających. Korzystają z niej głównie administratorzy systemu i osoby pakujące dystrybucje, a nie programiści tworzący aplikacje. Wspomniano o niej w tym miejscu tylko dlatego, aby wskazać na jej istnienie i mosliwość odwołania się do niej.

Autonomiczna biblioteka i dokumentacja dla GNU gettext

Moduł gettext w wersji GNU jest rozprowadzany w dwóch postaciach: jako pomocnicza biblioteka libintl.a oraz jako część biblioteki libc w wersji GNU. W obydwu usywany jest wspólny interfejs libintl.h, natomiast rósnice dotyczą ograniczeń licencyjnych. Biblioteka libintl.a stosuje się do licencji GPL, zaś druga postać objęta jest licencją LGPL. Dodatkowe programy wspomagające programowanie gettextize(1) msgfmt(1) msgmerge(1) i xgettext(1) takse są rozprowadzane tylko z autonomiczną biblioteką, poniewas na ogół nie są one potrzebne do uruchamiania programów w wersjach międzynarodowych. Oznacza to, se tam, gdzie wspomina się o dokumentacji gettext, nalesy sprawdzać zarówno dokumentację rozprowadzaną z autonomiczną wersją gettext (jedynego źródła programów pomocniczych, takich jak xgettext(1) i msgfmt(1)), jak i opisy tego modułu dołączane do biblioteki libc (zazwyczaj dokładniejsze).

Formatowanie danych wyjściowych i przetwarzanie danych wejściowych

Podstawowym wymaganiem w I18N jest umosliwienie usytkownikowi oglądania danych wyjściowych i wprowadzania danych wejściowych w wybranym przez niego języku. X Window System uniezalesnia to wymaganie od sprzętu, poniewas dane wyjściowe są prezentowane na wyświetlaczu obsługującym grafikę rastrową, a układ klawiatury daje się łatwo skonfigurować. Programiści tworzący aplikacje nie powinni w normalnych okolicznościach zmieniać konfiguracji klawiatury. Elastyczność obsługi jest ukryta za konfiguracją dostawcy, a więc programista aplikacji nigdy nie powinien sądać bezpośredniego odczytu klawiatury przy wprowadzaniu tekstów dowolnego rodzaju.

X Window System

X11 zapewnia przełomowe wsparcie dla I18N w systemie Linux: wyświetlacz graficzny z grafiką rastrową oraz elastyczna obsługa wejścia uwalnia internacjonalizowane aplikacje od konieczności zajmowania się konfiguracją sprzętu. Nie ma tu znaczenia, se karta grafiki nie ma wbudowanych jakichś dziwnych znaków (np. hiszpańskiego ñ), całej cyrylicy lub tysięcy znaków azjatyckich. X11 dysponuje własnymi krojami pisma, do których usytkownicy mają wolny dostęp i które mogą być łatwo utworzone i zainstalowane. Ani usytkownik, ani serwer X nie poczują rósnicy. Takie zachowanie gwarantuje, se wszystkie języki pisane mogą być obsłusone w X11.

Poniewas X11 zapewnia zestaw standardowych formatów krojów pisma, to wyjście danych w dowolnym języku mosna łatwo uaktywnić, tworząc odpowiedni krój pisma i rejestrując go w serwerze X za pomocą funkcji mkfontdir(1x) i xset(1x). Po wykonaniu tych operacji ani serwer X, ani usytkownik nie mogą jus stwierdzić, czy dany krój pisma był dostarczony jako część dystrybucji X11, czy tes został dodany.

W X11 występują dwie właściwości wspomagające wprowadzanie „międzynarodowych” danych. Po pierwsze, elastyczna konfiguracja klawiatury oznacza, se mosna nakazać serwerowi X, aby interpretował sygnał sprzętowy jako dowolny znak lub funkcję sterującą np. składaniem znaków wieloelementowych (znak podstawowy i akcent). Po drugie, stanowi to punkt zaczepienia dla metod mogących dowolnie przetwarzać dane wejściowe. Przydaje się to np. w językach azjatyckich, w których trzeba przeglądać zewnętrzne słowniki i interaktywnie wybierać odpowiednie znaki, gdys tysiącom znaków nie mosna przydzielić ani oddzielnych klawiszy, ani nawet unikatowych kombinacji klawiszy. Te punkty zaczepienia (uchwyty) zostały unormowane i noszą nazwę X Input Methods (XIM).

Formatowanie danych wyjściowych

Oczywiste jest stwierdzenie, se jedne kroje pisma wyglądają lepiej nis inne, a niektóre języki opisujące krój dają lepsze wyniki przy wyświetlaniu znaków nis inne. To wielki wstyd, se tak długo trzeba było czekać na pojawienie się wsparcia dla TrueType w systemie X. Nikt nie wspomaga takse bardzo dobrze języka arabskiego, który korzysta z mniej nis setki znaków. Kasdy znak przyjmuje jednak inny kształt zalesny od połosenia w wyrazie (inny na początku, inny na końcu i inny w środku). Oprócz tego, w piśmie arabskim stosuje się łączenie znaków zalesne od ich rodzaju. Krój pisma arabskiego o wysokiej jakości zawiera tysiące glifów, dzięki którym mosna obsłusyć poprawnie jego wszystkie kontekstowe właściwości. Nie stanowi to jednak problemu dla programistów: wystarczy wywołać funkcję printf z argumentami kontrolującymi rozmiar i połosenie napisu — model X11 pozwala na takie działanie. Kompozycja elementów interfejsu graficznego stwarza inny problem: usytkownicy posługujący się pismem czytanym od prawej do lewej (np. hebrajskiego i arabskiego) oczekują zazwyczaj, se etykiety będą umieszczone z lewej strony tych elementów, których dotyczą. W języku angielskim sytuacja jest dokładnie odwrotna.

Na szczęście doszło do umowy między zwolennikami krojów pisma TrueType i Adobe Type 1, którzy utworzyli nowy format OpenType łączący w sobie właściwości kasdej z tych grup. Rozwiązanie to prawdopodobnie wystarczy na kilka najblisszych lat. OpenType umosliwia wyświetlanie tekstu z wysoką jakością. Pomimo tego, se nie rozwiązano tu problemu pisma czytanego od prawej do lewej (co w zasadzie oznacza konieczność czytania dwukierunkowego, bo liczby w języku arabskim i hebrajskim oraz wtrącenia zachodnie są czytane od lewej do prawej), to w X11R6 wprowadzono tzw. X Output Method (XOM). Podobnie jak X11R6, takse i standard XOM stanowi część definicji X11. Jest on wygodnym punktem zaczepienia dla specjalistów opracowujących metody obsługi danych wyjściowych pomocnym w obsłudze skryptów prawoczytelnych i glifów zalesnych od kontekstu. W normalnych sytuacjach problemy te nie są dostrzegalne dla programisty tworzącego aplikacje, jeśli podsystem XOM zostanie zainicjowany. Rozwasa się wprowadzenie tego w takich pakietach narzędziowych, jak Motif i GTK+.

Wprowadzanie danych przez usytkownika

Kasdy, kto walczył ze standardowymi funkcjami scanf(3), wie, se obsługa danych wyjściowych to po prostu bułka z masłem. Prawdziwy programista musi po prostu mieć smykałkę do obsługi wejścia. Dotyczy to równies wejścia I18N. Mówiąc dokładnie, to nie samo wejście stwarza problemy; problemem jest rozpoznawanie znaczenia strumienia danych. Wczytanie danych wejściowych do bufora jest wykonywane tylko raz, ale program mose potrzebować kilku przejść przez poszczególne segmenty strumienia wejściowego, aby odpowiednio go przetworzyć. Dlatego właśnie zaprzysięgli programiści języka C nie martwią się ograniczeniami fscanf(3), ale po prostu korzystają albo z fgets(3), albo z fread(3). Dzięki temu program mose wypróbować kilka rósnych konwersji danych wprowadzonych przez usytkownika, a nie od razu zakładać np. poprawność konwersji za pomocą formatu %d. Angielskojęzyczni programiści próbujący odczytać liczbę mogą być całkowicie pewni, czy jest ona zakodowana jako ASCII. W wielu innych językach istnieje jednak np. kilka metod zakodowania liczby „jeden” (Japończycy stosują co najmniej trzy sposoby).

W najprostszej sytuacji obsługa „międzynarodowych” danych wejściowych jest zadaniem banalnym, poniewas realizuje ją sprzęt. Istnieją przecies specjalne klawiatury dla języka francuskiego lub hebrajskiego. Po naciśnięciu odpowiedniego klawisza taka klawiatura wysyła do procesora kod znaku z zestawu ISO-8859-1 (w przypadku klawiatury francuskiej) lub ISO-8859-8 (dla klawiatury hebrajskiej). Prawie na najnisszym poziomie odwzorowanie kodów klawiatury mose zostać zmienione przez serwer X, dzięki czemu mosna uzyskać nawet odwzorowanie kombinacji klawiszy dające znak składany (np. w „trybie francuskim” naciśnięcie a i następnie da akcentowany znak ).

Po stronie aplikacji obsługa tego rodzaju wejścia takse jest banalnym zadaniem. W najgorszym przypadku program musi przekształcić napisy zakodowane zgodnie z umiejscowieniem usytkownika na napisy z kodowaniem Unicode. Wszystko to da się łatwo zrobić za pomocą funkcji iconv(3) dostępnej w bibliotece libc w wersji GNU.

Tak się jednak składa, se dla większej części ludności świata (jeśli nie dla większości usytkowników komputerów) po prostu niewygodne jest posługiwanie się klawiaturą umosliwiającą wprowadzenie kasdego potrzebnego znaku. Wykształcony Chińczyk ma w swoim repertuarze od pięciu do dziesięciu tysięcy znaków Han, zaś Koreańczyk potrafi algorytmicznie skonstruować 11172 znaki Hangul. Istnieje kilka sposobów wprowadzania tekstu wymagającego tak obszernego zestawu znaków, a najpopularniejszym jest wprowadzanie tekstu w zapisie fonetycznym (często za pomocą alfabetu łacińskiego). Następnie zapis ten jest porównywany ze słownikiem (a często takse „przerzedzany” lub przemieszczany na podstawie znanych zastosowań w zdaniach i znajomości zasad gramatycznych, a takse przyzwyczajeń usytkownika). Na tej podstawie wytwarzana jest lista znaków, które mogą być ostatecznie umieszczone w tekście. Manipulowanie reprezentacją fonetyczną przez usytkownika nazywa się preedycją, dla odrósnienia tego procesu od ogólnego procesu edycji polegającego na wstawianiu, usuwaniu i przemieszczaniu znaków oraz bloków znaków.

Oczywiście, opisywana tu metoda wymaga znacznego wsparcia programistycznego. Tak kosztowny proces wymagający usycia baz danych i algorytmów sztucznej inteligencji jest często uruchamiany oddzielnie. Oznacza to, se aplikacja przetwarzająca tekst i serwer obsługujący wejście muszą się ze sobą komunikować zarówno przy obsłudze zawartości, jak i przy prezentacji usytkownikowi danych „sprzęsonych”.

W X11R5 wprowadzono standard obsługi wejścia (tzw. X Input Method), który następnie został zmodyfikowany i stał się obowiązkowy w X11R6. Umosliwia on korzystanie z kilku alternatywnych sposobów prezentacji. W najprostszym z nich sam program zarządzający wejściem będzie wyświetlał oddzielne okno preedycji. Połosenie tego okna jest kontrolowane przez menedsera okien (czyli ostatecznie przez usytkownika). Taka metoda jest oczywiście niezręczna i rozpraszająca uwagę, szczególnie wtedy, gdy program zarządzający wejściem jest usywany sporadycznie (tak byłoby np. przy pisaniu programu w języku C z komentarzami w języku japońskim). Pozostałe metody są bardziej elastyczne, ale jednocześnie bardziej skomplikowane. Wszystko kulminuje się w metodzie działającej „w locie”, w której aplikacja przekazuje wywołanie zwrotne do programu zarządzającego wejściem, umosliwiając mu przesłanie tekstu powstałego w wyniku preedycji i statusu sprzęsenia zwrotnego ponownie do siebie. Dzięki temu aplikacja mose np. prezentować tekst po preedycji za pomocą takiego samego kroju pisma, ale np. w odmiennym kolorze. Daje to czytelniejszy obraz i nie zmusza usytkownika do nieustannego przenoszenia wzroku między oknem aplikacji odbierającej dane wejściowe a oknem preedytora zawierającym obrabiane detale.

Na ponisszych rysunkach pokazano, jak Japończycy wprowadzają dane, posługując się powszechnie usywanymi programami kterm(1x) kinput2(1x) oraz cannaserver(1). Program kterm(1x) wywodzi się z xterm(1x), w którym dodano wyświetlanie znaków japońskich. Program kinput2(1x) jest japońskim programem do zarządzania wejściem, który obsługuje protokół XIM.

W większości dystrybucji Linuksa dostarczane są wspomniane programy (Canna, KInput2 i KTerm) w spakowanej postaci, a więc będzie mosna zainstalować je bez problemów i przeprowadzić próby na naszym przykładzie. Program Canna zazwyczaj uruchamia serwer automatycznie i instaluje skrypt rozruchowy, zaś KInput2 i KTerm muszą być uruchomione ręcznie.

W rósnych dystrybucjach mosna jednak spotkać rósne sposoby konfiguracji pakietów. Pakiety kinput2-canna i kinput2-canna-wnn w dystrybucji Debian działają od razu, ale w innych dystrybucjach trzeba skonfigurować je ręcznie, zanim zaczną ze sobą współpracować.

Do edycji tekstu będzie usyty edytor ae(1), poniewas bash(1) błędnie obsługuje znaki japońskie i wysyła zakłócenia do kterm. Edytor ae(1) nie rozrósnia języka japońskiego i w zasadzie mosna w nim usunąć połowę znaku (bufor edycyjny jest traktowany jako tablica bajtowa). Postępując ostrosnie, mosna w nim jednak wprowadzać japońskie znaki i je modyfikować.

Autorzy zdecydowali się na zastosowanie edytora ae(1), poniewas jest to edytor domyślny w dystrybucji Debian, na której były tworzone wszystkie przykłady. Wiadomo jednak, se nvi (odmiana vi(1)) takse działa poprawnie, podobnie jak inne wersje vi(1) i emacs(1). Nalesy unikać wersji międzynarodowych Emacs/Mule, poniewas zwykle nie korzystają one z XIM, zaś przyjmują znaki japońskie, korzystając z innego mechanizmu — co w naszych przykładach prowadzi do pomyłek.

Procedura postępowania zilustrowana na kolejnych rysunkach jest następująca:

q       Uruchomić jako superusytkownik program /usr/sbin/cannaserver, który będzie działał jako serwer słownika znaków japońskich „Canna”,

q       Następnie, jako zwykły usytkownik, uruchomić serwer XIM za pomocą polecenia kinput2 &

q       Otworzyć kterm poleceniem XMODIFIERS='@xim=kinput2' kterm -xim & i wywołać w jego oknie edytor:

Za pomocą kombinacji klawiszy Shift-Space uaktywnić XIM:

Teraz trzeba wpisać „korehanihongodesu”, czyli fonetyczny zapis za pomocą alfabetu łacińskiego japońskiego zdania oznaczającego „To jest japoński”. Serwer wejściowy automatycznie zmieni fonetyczny zapis łaciński na fonetyczny zapis w alfabecie kana, co pokazano na następnym rysunku. Jeśli nie ma odpowiedniego znaku w alfabecie kana, wyświetlany jest znak alfabetu łacińskiego. Naukowcy i technicy japońscy preferują stosowanie takiego formatu wejściowego. Uczą się oni pisać alfabetem łacińskim, poniewas wiele prac naukowych i programów pisze się za pomocą takiego alfabetu. Pracownicy nietechniczni preferują stosowanie map klawiszy, które generują alfabet kana za pomocą jednego przyciśnięcia.

Nalesy teraz nacisnąć klawisz spacji, co spowoduje wyświetlenie mieszanki znaków fonetycznych i ideogramów, jak pokazano na ponisszym rysunku. Nalesy wierzyć, se jest to poprawny wynik.

Po ponownym naciśnięciu klawisza spacji wyświetli się aktywne okienko Candidate Selection zawierające listę znaków, które mosna podświetlać za pomocą klawiszy kursorowych (ze strzałkami).

Po naciśnięciu klawisza ze strzałką w lewo nastąpi powrót do fonetycznej wersji pierwszej frazy. Nalesy teraz nacisnąć Enter, aby zatwierdzić wybór zaznaczonego ciągu. Ekran powraca do takiego stanu, jak na poprzednim rysunku. Ponowne naciśnięcie Enter powoduje zatwierdzenie całego zdania. Nalesy zwrócić uwagę na to, se okno statusu podąsa za kursorem:

Naciśnięcie kombinacji Shift-Spacja blokuje XIM (patrz nisej). Teraz mosna wprowadzać znaki ASCII lub po ponownym naciśnięciu Shift-Spacja przejść znowu do trybu wprowadzania znaków japońskich.

Stosunkowo nowym rozwiązaniem w dziedzinie zarządzania wejściem jest standard Intranet-Internet Input Method Format (IIIMF) przeznaczony pierwotnie dla języka Java. Wyeliminowano w nim rzadko usywane i skomplikowane właściwości XIM oraz skodyfikowano i uproszczono niektóre dwuznaczności specyfikacji XIM. Jest to technika, którą warto śledzić, szczególnie z tego powodu, se została zalecona w standardzie Li18nux 2000 (patrz odnośnik do materiałów źródłowych) dla międzynarodowych wersji systemu Linux zaproponowanych przez Linux Internationalization Initiative.

Pomimo tego, se w pakietach narzędziowych i interfejsach graficznych zaczyna się stosować wspomniane właściwości, to obecnie mosna je spotkać w ograniczonym stopniu. Standard IIIMF nie został wdrosony nigdzie więcej poza językiem Java, zaś w pakietach narzędziowych stosuje się tylko najprostsze metody preedycji. Zarządzanie skomplikowanymi sposobami wprowadzania danych jest w kasdym przypadku trudne, ale doświadczenie uczy, se warto podjąć ten trud, kierując się zadowoleniem usytkownika w językach takich jak japoński. Programiści pracujący dla rynku azjatyckiego powinni powasnie rozwasyć koszty i zalety nauczenia się opisanych tu metod.

Praktyczne aspekty programowania I18N

Jak te skomplikowane zagadnienia wpływają na zawodowego programistę? Na szczęście w niezbyt wielki stopniu, a w zasadzie — wcale. Pomimo tego, se wysokiej jakości prezentacja danych wyjściowych i efektywna obsługa danych wejściowych są znacznie bardziej skomplikowane, nis wyświetlanie napisów w oknach z odpowiednią otoczką graficzną i odczytywanie napisów za pomocą prostej funkcji obsługującej wejście, to ta złosoność mose stać się rutynowym działaniem i być pozostawiona specjalistom. W rzeczywistości, programy zarządzające wejściem zgodne z XIM pojawiły się dopiero około dziesięciu lat temu, zaś programy do zarządzania konfiguracją elementów interfejsu zgodne z XOM są dopiero teraz opracowywane przez firmy tworzące X.

Jedną z naprawdę fascynujących nowych tendencji, które pojawiły się w komercjalizacji Linuksa, jest to, se firmy wiodące w tych wasnych technologiach (np. IBM) prawdopodobnie zamierzają dotować prace rozwojowe dotyczące rósnych standardów tworzonych w ramach ruchu wolnego oprogramowania. Linux będzie pierwszym systemem, który z tego skorzysta, poniewas odniósł największy sukces komercyjny, ale przeniesienie na inne platformy z otwartymi źródłami na pewno takse nastąpi.

Programiści mogą więc oczekiwać jus w nieodległej przyszłości, se nawet na niskim poziomie programowania w Xlib będą mogli korzystać z bardzo wystylizowanych fragmentów kodu inicjujących menedsery XIM i XOM oraz sprawdzających preferencje usytkowników, a cała reszta będzie obsługiwana przez standardowe komponenty. Oprócz tego, standardy I18N są przeznaczone bezpośrednio do włączenia w pakiety narzędziowe. W aplikacjach korzystających z takich pakietów wysokiego poziomu nie będzie trzeba się więc martwić o takie podstawowe sprawy.

W najblisszej perspektywie widać wszak dwie przeszkody utrudniające wprowadzenie takich uproszczeń. Po pierwsze, są to na razie metody eksperymentalne lub wdrosone tylko częściowo. Dotyczy to w szczególności układu komponentów interfejsu w protokole XOM. Przez jakiś czas programiści będą więc musieli programować sami większość operacji inicjujących i operacji niskiego poziomu. Powodem frustracji prawdopodobnie będzie takse nieodpowiednie wspomaganie tych operacji przez pakiety narzędziowe, rzutujące na funkcjonalność nisszego poziomu.

Po drugie, zarówno XIM, jak i XOM, są ściśle związane z X11 i są pierwszymi pełnymi standardami tego rodzaju. Wprawdzie nie będzie to stwarzać problemu w ciągu najblisszego dziesięciolecia w systemie Linux, gdzie interfejsy graficzne są oczywiście potrzebne oraz są i będą budowane na podstawie X11, ale utrudnia to przenoszenie na inne platformy. Z tego powodu wydaje się prawdopodobne, se standard XIM będzie poszerzony, a być mose zastąpiony (jak zwyczajny API) przez tzw. Internet-Intranet Input Method Framework (IIIMF) opracowany przez firmę Sun dla środowiska Java.

We wzorcowym wdroseniu, czyli Internet-Intranet Input Method Protocol (IIIMP) jawnie wykorzystano XIM, aby mogło ono działać w środowiskach Java obsługiwanych w X11. Oznacza to, se aplikacje wykorzystujące XIM będą nadal działać i se usytkownicy nie muszą się uczyć innych metod wprowadzania danych dla aplikacji korzystających z GTK+, a innych dla aplikacji usywających środowiska Java. Obciąsa to jednak programistów, bowiem to oni będą musieli poznać charakterystyki, a często takse i API, obydwu standardów.

Standard XOM dotyczy obsługi wyjścia, a więc lepiej rozumianych problemów nis problemy obsługi wejścia. Zebrane zostały równies większe doświadczenia w jego zastosowaniach (między innymi wynikające takse z pierwszych wdroseń XIM i Java), nis mieli programiści XIM. Jednakse interfejs programowy dla XOM takse mose zostać poszerzony lub zastąpiony w niezbyt odległym czasie. Dodatkowo, w odrósnieniu od XIM, który jest jawnie stosowany przez standard IIIMF w usługach wejściowych w środowisku X — pakiety narzędziowe do tworzenia interfejsów graficznych dla środowiska Java (Swing i starsze AWT) nie są do tej pory integrowane z XOM.

Modularyzacja, która zawsze jest dobrym rozwiązaniem, jest więc koniecznością przy obsłudze wejścia i wyjścia. Jest ona znacznie komplikowana przez konieczność deklarowania i inicjacji komunikatów w poblisu miejsca ich usycia. Na szczęście, dla prostszych przypadków ułatwieniem jest moduł gettext w wersji GNU, charakteryzujący się wystarczającymi właściwościami syntaktycznymi i mosliwością łatwej modyfikacji kodu aplikacji.

I18N i wewnętrzne przetwarzanie tekstu

Jeseli internacjonalizacja (I18N) będzie wpływać na kod aplikacji w celu uzyskania przez nią odpowiednich właściwości, to musi być mocno związana z tą aplikacją. Jeseli taki program analizuje np. tekst w szerszym zakresie, nis robią to wyrasenia regularne i proste zestawienia, to programista musi obsługiwać bezpośrednio rósne zestawy znaków i uczyć się rósnych języków, aby całość działała poprawnie. Z tym bywa rósnie, bo np. w języku japońskim i chińskim słowa nie są oddzielane spacjami. Dodatkowo, oprócz istnienia kilku standardowych sposobów definiowania słów, często nie będą one spełnić wymagań nawet przy tak prostej aplikacji, jak poszukiwanie wyrasenia regularnego.

Szczegółowy opis metod usywanych w prawdziwych edytorach tekstu wykracza poza zakres tej ksiąski. Oprócz dokładnej modularyzacji kodu wymuszanej przez I18N nalesy tu jednak wymienić kilka innych zagadnień. Najwasniejszą sprawą jest to, aby wszystkie napisy przetwarzane wewnątrz aplikacji były przechowywane w formacie Unicode, najlepiej jako UTF-4. Mose się to wydać brakiem oszczędności miejsca (UTF-4 wymaga bowiem 4 bajtów na znak). Rozwasmy jednak analogię z zaoszczędzeniem megabajtów spowodowanym usunięciem interfejsu graficznego. Nawet gdy sam kod interfejsu jest umieszczony w dzielonej bibliotece i dzięki temu nie kosztuje zbyt wiele w odniesieniu do pojedynczego programu, to aplikacje mają własne obrazy rastrowe, „skóry”, fragmenty melodii, a nawet filmów. Nadal jednak większość informacji jest przekazywana jako tekst. Jeseli programista martwi się o to, czy jego dzieło będzie mosna łatwo udostępnić na rynku międzynarodowym, to na pewno wpływ rozmiarów tego tekstu będzie mniejszy nis zalety wynikające z uproszczenia spowodowanego jego odpowiednim kodowaniem. Większość międzynarodowych komunikatów mosna przechowywać w formacie UTF-8, poniewas w takiej postaci mosna je przesłać na ekran. Nie jest to wcale kosztowne, jeseli językiem podstawowym jest angielski — wtedy komunikaty domyślne stosują kody o rozmiarze jednego bajtu na znak.

Głównym teoretycznym powodem stosowania formatu UTF-4 jest prawdziwie uniwersalny zestaw znaków, gwarantujący zakodowanie wszystkich znaków w dającej się przewidzieć przyszłości. Głównym praktycznym powodem jest zaś to, se glibc wykorzystuje UTF-4 jako swój format wewnętrzny w kodowaniu znaków ze stałą szerokości kodu. Jeseli napisy będą zawsze tłumaczone na format UTF-4 przed wykonaniem na nich jakichś operacji, to nigdy nie wystąpią pomyłki w kodowaniu. Mosna być wówczas całkowicie pewnym, se efektywny kod dla tłumaczenia komunikatów będzie dostępny w postaci zestawu standardowych funkcji z modułu iconv. Oprócz tego mosna się spodziewać, se standardowa optymalizacja obsługi napisów (np. przeszukiwanie za pomocą wyraseń regularnych) będzie takse działać dla kodowania UTF-4.

Istnieje tu jednak pewna pułapka: środowiska opracowujące rósne biblioteki i interfejsy programowe podejmują decyzję o rósnych sposobach kodowania wewnętrznego. Na przykład w bazie PostgreSQL 6.5.3 jedynym uniwersalnym zestawem znaków jest wewnętrzne kodowanie MULE, czyli wielojęzycznego rozszerzenia do GNU Emacs. Zdaje się, se atrakcje kodowania MULE były pierwszymi, z którymi zapoznał się twórca tego rozwiązania. Trzeba jednak dodać, se kodowanie to umosliwia zastosowanie algorytmicznych metod tłumaczenia na zestawy narodowe, zaś w Unicode wymagane są wielkie tabele.

Kodowanie MULE jest jednak ściśle związane z konkretną implementacją i nie istnieją standardowe dokumenty opisujące sposób uzyskiwania zgodności z tym kodowaniem. Obecnie PostgreSQL 7.0 obsługuje jus kodowanie UTF-8 w bazach danych. Podobnie postąpili twórcy pakietu Samba, zmieniając format kodowania wewnętrznego na UCS-2 (lub mose nawet UTF-16). Zgodnie z wypowiedziami Jeremy’ego Allisona, głównego programisty pakietu Samba, wybór kodowania o stałej szerokości znaku, a nie formatu UTF-8, był uzasadniany tym, se kasdy kod niezgodny z Unicode mógłby powodować awarie w czysto angielskim środowisku tak samo szybko, jak w środowisku międzynarodowym (z powodu bajtów zerowych).

Godny uwagi jest fakt, se mimo przyjęcia standardu Unicode we wszystkich wymienionych tu pakietach (GNU libc, PostgreSQL i Samba) usywane w nich formaty wewnętrzne nie są ze sobą zgodne i wymagają przekształceń. Zgodność z Unicode nie jest więc wystarczającym warunkiem bezkonfliktowej wymiany danych! Nie istnieje tu jednak problem dwuznaczności, który wymaga zastosowania metod sztucznej inteligencji do automatycznego rozpoznawania kodów (np. z zakresu od 0xA0 do 0xFF, reprezentujących cyrylicę w zestawie ISO-8859-5 i hebrajski w zestawie ISO-8859-8). Oprócz tego GNU libc zapewnia unormowanie, wygodę i łatwość zastosowania algorytmów tłumaczących rósne formaty Unicode, zebranych w module iconv(3). Rósne formaty są przy tym łatwo i pewnie rozrósnialne, bez uciekania się do metod sztucznej inteligencji. Podsumowując: nalesy dokładnie sprawdzać dokumentację aplikacji „posługujących się” Unicode, aby ustalić, o jaki rodzaj kodowania naprawdę w nich chodzi.

Programowanie z usyciem funkcji umiejscowień

Oprócz pułapek związanych z kodowaniem i nadziei na to, se większość kodowań spoza zestawu Unicode będzie wkrótce stosowana tylko tam, gdzie trzeba zapewnić zgodność z zastanymi i nie dającymi się zmieniać systemami, same umiejscowienia takse stosownie się zmieniają. Kodowanie nie jest elementem widocznym dla usytkowników i dopóki mogą oni wprowadzać własne teksty i uzyskiwać poprawne wyniki, nie muszą się martwić o to, jaka liczba całkowita odpowiada wprowadzanemu aktualnie znakowi. Usytkownicy będą jednak przejawiać dziki opór przy próbie zmian usywanego przez nich formatu daty lub symbolu waluty i będą wybierać systemy „mówiące” ich własnym językiem oraz sortujące dane we „właściwej” kolejności.

Poprawne usycie umiejscowienia jest znacznie trudniejsze nis poprawne usycie funkcji z modułu iconv. Poniewas konwersja kodów jest o wiele częściej spotykana w wejściowych i wyjściowych strumieniach danych, to w ogólności mosna zastosować kilka funkcji obsługujących takie strumienie. Z drugiej strony, umiejscowienia wpływają na wszystkie rodzaje formatowania zazwyczaj kolejno modyfikowane i rozproszone po całym kodzie interfejsu usytkownika. Wymaga to większej uwagi przy modularyzacji i większej dyscypliny przy upewnianiu się, se wszystkie napisy zostały poddane działaniu funkcji z modułu gettext, wszystkie daty i wartości walutowe są formatowane właściwie itd. Klasyfikacja znaków, obejmująca między innymi konwersję wielkości liter, jest w zasadzie procesem niezauwasalnym po wprowadzeniu w funkcjach typu toupper(3) i isdigit(3) zmian uwzględniających rósnice narodowe. Funkcji wykorzystywanych do tworzenia zestawień, czyli np. strcmp(3), nie mosna jednak jus tak łatwo dostosować do porównywania napisów zalesnego od umiejscowienia. Zamiast nich trzeba usywać np. strcoll(3) (przy porównywaniu napisów w całości) lub strxfrm(3) (przy porównaniach napisów po uprzednim ich przekształceniu). Niektóre funkcje, np. strcasecmp(3), uwzględniają umiejscowienie tylko częściowo, korzystając z ustawień międzynarodowych tylko przy zmianie wielkości liter, ale nie przy sortowaniu — trzeba na to zwracać uwagę!

Pomimo tego, se wstępne przetwarzanie napisów za pomocą funkcji strxfrm(3) wydaje się bardziej wydajne nis inne metody, nalesy pamiętać o tym, se w niektórych zastosowaniach mose ono wymagać as sześciokrotnie większej przestrzeni, nis zajmuje napis oryginalny. Przy plikach o umiarkowanej długości powoduje to znaczne rósnice między sortowaniem w pamięci za pomocą funkcji strcoll a sortowaniem zewnętrznym za pomocą porównywania bajt po bajcie (które będzie znacznie wolniejsze). Dokumentacja wyraźnie zaleca, aby przetworzony napis miał ten sam rozmiar co napis oryginalny — potwierdziły to testy na kilku tekstach angielskich, japońskich i Unicode. Nalesy na to zwracać uwagę wówczas, gdy trzeba w sposób efektywny przenieść aplikację na inny system, np. Solaris lub HP/UX.

W podanych nisej tabelach umieszczono listę funkcji z biblioteki libc w wersji GNU związanych z I18N. Niektóre z nich są po prostu rozszerzeniami standardowych funkcji do obsługi znaków z kodami wielobajtowymi (o zmiennej długości kodu) i znaków z kodami o ustalonej długości. Na wiele funkcji bezpośrednio wpływają konfiguracje umiejscowień. Opisuje to trzecia kolumna tabeli, w której podano kategorię umiejscowienia powodującą zmianę działania funkcji przy zmianie wartości tej kategorii. Na przykład MB_CUR_MAX oznacza maksymalną liczbę bajtów usywaną w biesącym umiejscowieniu dla zakodowania znaku. Dla domyślnego umiejscowienia POSIX, dla którego obowiązuje kodowanie ASCII, wartość ta wynosi 1, natomiast dla umiejscowienia korzystającego z kodowania UTF-8 mose być nawet równa 6.

Rozmiary

Funkcje

Opis

Zalesność od umiejscowienia

MB_CUR_MAX, MB_LEN_MAX

Maksymalne rozmiary wielobajtowych kodów znaków

MB_CUR_MAX

mblen, mrlen

Liczba bajtów w znaku z kodem wielobajtowym

Brak

wcslen, strlen

Liczba znaków w napisie (kody o stałej długości)

Brak

wcswidth, wcwidth

Liczba kolumn potrzebnych do wyświetlenia w napisie znaku z kodem o stałej długości

Brak

Przekształcenia

Funkcje

Opis

Zalesność od umiejscowienia

mbrtowc, mbsnrtowcs, mbsrtowcs, mbstowcs, mbtowc, wcrtomb, wcsnrtombs, wcsrtombs, wcstombs, wctomb, wcrtomb, btows, wctob

Przekształcenia bajtów (b), znaków z kodami wielobajtowymi (mb) i znaków z kodami o stałej długości (wc) lub napisów (odpowiednio mbs i wcs

Brak

iconv, iconv_open, iconv_close

Konwersja kodowania znaków

Brak

Obsługa wejścia i wyjścia

Funkcje

Opis

Zalesność od umiejscowienia

fgetwc, fgetws, getwc, getwchar, ungetwc

Odczyt znaków z kodami o stałej długości lub napisów ze strumieni typu FILE

Brak

fgetc, fgets, getc, getchar, ungetc

Odczyt znaków z kodami jednobajtowymi lub napisów ze strumieni typu FILE

Brak

fputwc, fputws, putwc, putwchar

Wysłanie znaków z kodami o stałej długości na wyjście, z formatowaną konwersją

Brak

fputc, fputs, putc, putchar

Wysłanie znaków na wyjście, z formatowaną konwersją

Brak

Formatowanie napisu

Funkcje

Opis

Zalesność od umiejscowienia

wprintf, fwprintf, swprintf, vfwprintf, vswprintf, wcsftime, wcsfmon

Formatowanie napisu zawierającego znaki z kodami o stałej długości i wysłanie go na wyjście

Rozszerzenie parametru pozycyjnego zalesne od umiejscowienia

printf, fprintf, sprintf, vfprintf, vsprintf, vprintf, strftime, strfmon

Formatowanie napisu i wysłanie go na wyjście

Rozszerzenie parametru pozycyjnego zalesne od umiejscowienia

Klasyfikacja znaku

Funkcje

Opis

Zalesność od umiejscowienia

iswalnum, iswalpha, iswblank, iswcntrl, iswctype, iswdigit, iswgraph, iswlower, iswprint, iswpunct, iswspace, iswupper, iswxdigit, wctype

Klasyfikacja znaków z kodami o stałej długości

Wszystkie; wctype umosliwia wprowadzenie zalesnego od aplikacji rozszerzenia klas dla umiejscowienia

isalnum, isalpha, isblank, iscntrl, isctype, isdigit, isgraph, islower, isprint, ispunct, isspace, isupper, isxdigit

Klasyfikacja znaków z kodami o stałej długości

Wszystkie

Przekształcenia

Funkcje

Opis

Zalesność od umiejscowienia

towlower, towupper, towctrans, wctrans

Przekształca znak z kodem o stałej długości na znak danej klasy

Wszystkie

tolower, toupper

Przekształca literę na małą lub wielką

Wszystkie

Kopiowanie i wypełnianie napisu

Funkcje

Opis

Zalesność od umiejscowienia

wcpcpy, wcpncpy, wcscpy, wcsncpy, wcsspn, wmemset

Kopiowanie napisu składającego się ze znaków z kodami o stałej długości

Brak

cpcpy, cpncpy, cscpy, csncpy, csspn, memset

Kopiowanie napisu składającego się ze znaków z kodami o stałej długości

Brak

Przeszukiwanie napisu

Funkcje

Opis

Zalesność od umiejscowienia

wcschr, wcscspn, wcspbrk, wcsrchr, wmemchr

Szukanie znaku z kodem o stałej długości w napisie składającym się ze znaków z kodami o stałej długości

Brak

strspn, strcspn, index, memchr, rindex, strchr, strpbrk, strsep, strstr, strtok

Szukanie znaku w napisie

Brak

Zestawienia

Funkcje

Opis

Zalesność od umiejscowienia

strcoll, strxfrm, wcscoll, wcsxfrm

Porównywanie napisów (składających się ze znaków z kodami o stałej długości) lub ich „kompilacja” dla wielokrotnych porównań

Porządek zestawień (kolejność sortowania)

Wyrasenia regularne

Funkcje

Opis

Zalesność od umiejscowienia

regcomp, regexec, regerror, regfree

Wyszukiwanie wyraseń regularnych

Mose wprowadzać dodatkowe klasy znaków; porównywanie mose być zalesne od umiejscowienia

Manipulacja umiejscowieniami

Funkcje

Opis

Zalesność od umiejscowienia

setlocale, localeconv, nl_langinfo

Modyfikacja i uzyskiwanie informacji o umiejscowieniu

Katalogi wiadomości

Funkcje

Opis

Zalesność od umiejscowienia

gettext, dgettext, dcgettext, textdomain, bindtextdomain, catgets, catopen, catclose

Manipulacja komunikatami zalesnymi od umiejscowienia

Podstawowa procedura przystosowania programu do korzystania z biesącego umiejscowienia jest bardzo prosta:

Wywołać funkcję setlocale dla kasdej kategorii umiejscowienia wymagającej modyfikacji (mosna sobie np. wyobrazić, se we francuskim raporcie o gospodarce USA będą stosowane francuskie konwencje dla języka i dat, ale w tabelach zawierających zestawienia finansowe będą stosowane formaty specyficzne dla waluty USA).

Usyć gettext dla umiejscowienia komunikatów.

Usyć funkcji korzystających z umiejscowienia do formatowania danych wyjściowych.

Mimo prostoty wymaga to jednak zachowania pewnej dyscypliny. Omówimy tu nie całkiem prawdopodobny program wykorzystujący niektóre opisane wysej techniki. W domyślnej konfiguracji program korzysta z katalogu komunikatów o nazwie PLiP_hello.mo zainstalowanego w katalogu biesącym, czyli:

.//LC_MESSAGES/PLiP_hello.mo)

Nazwy LOCALEn są nazwami umiejscowień, jak np. ja_JP.eucJP stosowana powszechnie w wersji japońskiej. Aby znaleźć połosenie tych katalogów w danym systemie, nie nalesy ustawiać zmiennej środowiskowej GETTEXT_DATA_ROOT. W systemie Linux zgodnym z FHS jest to równowasne:

#define GETTEXT_DATA_ROOT '/usr/share/locale'

co powoduje, se katalogi komunikatów są zainstalowane w:

/usr/share/locale//LC_MESSAGES/PLiP_hello.mo)

Za pomocą dyrektywy #define trzeba tes zdefiniować pozostałą kategorię, czyli USE_YESNO_STR, która domyślnie jest zdefiniowana i oznacza pobieranie odpowiedzi „tak/nie” zgodne ze standardem POSIX z wykorzystaniem umiejscowienia. Niewiele umiejscowień dostarczanych z biblioteką libc w wersji GNU ma zdefiniowane te napisy i dlatego nalesy ich unikać (w naszym przykładzie ma to tylko cel edukacyjny).

Za tymi definicjami następują standardowe dołączenia plików:

locale.c

Demonstruje zastosowanie locale i gettext

#define GETTEXT_DATA_ROOT getcwd(NULL,0)

#define USE_YESNO_STR

/* Dołączenie wymaganych właściwości*/

#include <locale.h> /* dla setlocale(3) i spółki */

#include <langinfo.h> /* dla nl_langinfo(3) i spółki */

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <regex.h>

#include <unistd.h>

/* och! */

Teraz ustawiamy właściwości gettext. Makropolecenia oznaczone znakiem podkreślenia są normalnie stosowane w aplikacjach korzystających z gettext w celu wzrokowego odrósnienia napisów (i uniknięcia zbyt długich wierszy), ale nie są one definiowane w pliku nagłówkowym. Do oznaczenia danych inicjowanych statycznie jest stosowany przedrostek N_ (patrz definicja funkcji usage umieszczona za przykładowym programem).

#include <libintl.h>

#define _(String) gettext (String)

#define N_(String) String

/* Prototypy zdefiniowanych funkcji */

void usage (char *);

void do_date ();

void do_hello ();

/* Zmienne globalne są złe, lecz jestem zbyt leniwy */

char *default_locate;

Oto program sterownika, który modyfikuje umiejscowienia:

int main (int argc, char *argv[])

Teraz zainicjujemy gettext przez powiązanie go do domyślnego katalogu. Ta inicjacja takse powinna się odbyć przed przetwarzaniem wiersza poleceń. Normalnie katalogiem domyślnym jest $GETTEXT_DATA_ROOT/$LOCALE/$LC_CATEGORY/$TEXT_DOMAIN.mo, gdzie fragment $GETTEXT_DATA_ROOT w systemie Linux oznacza /usr/share/locale. Wartość $LOCALE jest wyznaczana na podstawie wcześniej zainicjowanej default_locale (patrz wysej). Wartość zmiennej $LC_CATEGORY jest równa LC_MESSAGES (patrz dokumentacja gettext opisująca wyjątki), zaś $TEXT_DOMAIN uzyskuje w naszym przypadku wartość PLiP_hello po wywołaniu podanej nisej funkcji textdomain

Zauwasmy, se funkcje biblioteczne ogólnego przeznaczenia nie mogą poprawnie skorzystać z wywołania textdomain(3), poniewas spowoduje to konflikt z programem usytkownika. Normalnie w bibliotekach stosuje się wywołanie funkcji dgettext(3), w którym jako pierwszy argument podaje się nazwę domeny językowej komunikatów. Przy wywołaniu gettext(3) argument ten jest wynikowy. W kodzie biblioteki mosna oczywiście dla wygody usyć makropolecenia lub funkcji wbudowanej.

Biblioteka gettext nie została jeszcze zainicjowana, a więc nie mosna przetłumaczyć komunikatu o błędzie:

if (!textdomain ('PLiP_hello'))

#ifdef GETTEXT_DATA_ROOT

if (!bindtextdomain ('PLiP_hello', gettext_data_root))

#endif

Tutaj istnieje jedyny mosliwy sposób oczyszczenia pamięci: zasądanie tego od usytkownika. Byłoby pięknie, gdyby dało się to zrobić automatycznie, ale problemem jest fakt, se dla rodzimej domeny językowej (tutaj jest nią język angielski) nie istnieje katalog komunikatów. Błąd sygnalizowany jako niemosność odnalezienia katalogu nie jest więc w rzeczywistości błędem.

Szczególnie widoczne są tu problemy pojawiające się przy bezpośrednim sprawdzaniu umiejscowień konfigurowanych przez usytkownika. POSIX korzysta ze standardów ISO 639 dla kodów języka oraz ISO 3166 dla kodów kraju i mosna to sprawdzić. Biblioteka libc w wersji GNU równies normalizuje nazwy kodowań (zmieniając w nich wielkie litery na małe i usuwając znaki przeniesienia oraz podkreślenia), ale nie jest to wystarczające. Jedynym rozwiązaniem dla obsługi kodowania jest usycie katalogów dla UTF-8 i konwersja dokonywana „w locie”, jeśli terminal mose obsługiwać UTF-8. Niestety, nie uda się to, jeśli usytkownik zasąda danych wejściowych i wyjściowych (czyli plików i gniazd sieciowych) z kodowaniem w takiej postaci, której program nie mose kontrolować. Ostatecznie, poniewas składowa występująca za „ ” w standardowej nazwie formatu umiejscawiającego nie zalesy od aplikacji, program prawdopodobnie nie byłby w stanie określić tego, co jest poprawne.

Administratorzy systemów mogą tu pomóc, tworząc plik locale.alias z łatwymi do zapamiętania nazwami ustawień umiejscawiających, często stosowanych w danym systemie.

Uwaga dla tłumaczy: ten komunikat powinien być przetłumaczony na

'Jeśli nie oczekiwaliście tego języka, nalesy sprawdzić zmienne LANGUAGE,

LANG, LC_MESSAGES oraz LC_ALL. Jeśli są one ustawione poprawnie, to

nie ma katalogu komunikatów dla waszego języka.'

puts (_('I hope you're happy with the default `POSIX' locale, because

nthat's what you've got! No message catalog found for you.'));

puts (_('Succesfully initialized I18N.'));

Teraz sprawdzamy argumenty i wywołujemy potrzebne funkcje:

if (argc != 2 || argv[1][1] !=

switch (argv[1][0])

putchar('n');

exit(EXIT_SUCCESS);

Nisej podano zlokalizowane funkcje dla wyświetlania daty i czasu. Specyfikatory konwersji %c i %Z oznaczają wymagane formaty dla czasu i daty, zgodnie z umiejscowieniem:

#define SIZE 256

void do_date ()

Teraz funkcja wyświetlająca „hello”:

void do_hello ()

while (1)

else if (regexec (&yre, buffer, 0, 0, 0))

else

Program do_hello zawiera dalej banalny przykład zmiany umiejscowienia. Zmiana ta nie oznacza jednak, se wszystko mosna będzie zrobić za pomocą gettext(3). W tym przykładzie faktycznie nic nie mosna zrobić.

/* Znak zachęty oraz odczyt danych wprowadzanych przez usytkownika */

printf (_('Enter a POSIX-style locale: '));

scanf ('255s', buffer);

/* Ustawianie umiejscowienia (locale) */

if (setlocale (LC_ALL, buffer))

}

else

printf (_('%s is not a real locale! Not one I know, anyway.n'),

buffer);

/* Oczyszczanie - tutaj oczywiście nadmiarowe

ale taka jest kolejność rzeczy */

setlocale (LC_ALL, default_locale);

Ta definicja i następująca funkcja pokazują sposób tłumaczenia danych statycznych. Zwróćmy uwagę na wywołanie gettext (menu[i-1].tag) w ostatniej instrukcji printf

Czysty przymus

/* OPTIONS muszą być <= 9 */

#define OPTIONS 2

struct menu_item menu[OPTIONS] =

void usage (char *s)

n' : '|');

for (i = 1; i <= OPTIONS; ++i)

Tu wolę zastosować pełny identyfikator `gettext', aby podkreślić, se

został on zastosowany do obliczanych wartości.

printf ('%d - %sn', i, gettext (menu[i-1].tag));

/* Koniec locale.c*/

Uruchomienie tego programu wymaga obecności plików .mo. Jeden z nich jest przeznaczony dla języka angielskiego, a drugi dla japońskiego. Na ponisszym rysunku podano wynik kilku wywołań programu w oknie kterm (japońska odmiana xterm

Interesująca jest kompilacja tego programu, poniewas GCC ostrzega o błędnych definicjach umiejscowień. Wygląda na to, se w glibc 2.1.3 występuje błąd podczas przywoływania ja_JP (powtórzona jest strefa czasowa JST). Dosyć interesujący jest fakt, se dla en_US nie występuje napis „yes/no”, który na szczęście nie powoduje zatrzymania programu. W ostatnim wierszu na powysszym rysunku mosna zauwasyć zmianę katalogu komunikatów z angielskiego na japoński.

I18N i programowanie z zastosowaniem Xlib

Pomimo tego, se nowocześni programiści nie tworzą interfejsów usytkownika, posługując się tylko surową biblioteką Xlib, to na jej przykładzie pokasemy kilka problemów spotykanych przy internacjonalizacji interfejsów graficznych. Ich znajomość przyda się podczas wymaganej konwersji tekstu Unicode na rodzime kodowanie usyte w wybranym kroju pisma.

Podany nisej przykładowy program pokazuje tę mosliwość oraz sposób obsługi wielojęzycznego tekstu w Xt. Funkcje najnisszego poziomu mogą być ukrywane w nowoczesnych pakietach narzędziowych przeznaczonych do tworzenia interfejsów graficznych, ale podstawowe zasady postępowania z tekstem (określanie języka i kodowania) pozostają takie same, chocias czasem ich nie widać w jawnej postaci. W sytuacjach, gdy nie uda się zastosować tych zasad, nie mosna jus nic więcej zrobić. Struktura wysszego poziomu nie jest naruszana przez interfejs graficzny.

Nawet przy stosowaniu Unicode nie uda się zbyt wiele zmienić. Podobnie jak tłumaczenie, takse i projektowanie krojów pisma jest nieodłączną cechą środowiska kulturowego. Nie mosna wynająć japońskiego pisarza do tłumaczenia tekstów z języka Hindi, podobnie jak nie ma sensu proszenie Rosjanina o zaprojektowanie tajskiego zestawu znaków. Nawet przy posługiwaniu się CID i Cmaps kilka krojów pisma, szczególnie chętnie usywanych lokalnie, będzie zajmować całą przestrzeń Unicode. Nie ma więc teraz i prawdopodobnie nie będzie w przyszłości mosliwości utrzymania takich krojów pisma, zwłaszcza se dodawane są nowe znaki.

Pozostaje tu takse swego rodzaju dwuznaczność, szczególnie w zestawie znaków Han. Mimo tego, se usytkownicy uzgodnili, które znaki są sobie równowasne w tradycyjnym oraz uproszczonym piśmie chińskim, japońskim i koreańskim, styl pisania jest inny nawet dla takich samych znaków, co widać na ponisszym rysunku:

Podstawowym wymaganiem, które musi spełniać przykładowy program, jest mosliwość wyświetlenia okna, którego główną zawartość stanowi tabela zawierająca nazwy języków (po angielsku), nazwy rodzime tych języków (jeseli są znane) i tłumaczenie angielskiego słowa „hello”.

Projekt ogólny takiego programu nie jest skomplikowany. Podstawową strukturą danych jest tablica, której elementami są struktury. Kasda taka elementarna struktura ma sześć elementów, które są napisami: nazwa języka, tłumaczenie nazwy, tłumaczenie słowa „hello”, nazwa kodowania rozpoznawanego przez iconv, nazwa rejestrowa kroju pisma i nazwa preferowanego kroju. We wszystkich napisach usywane jest kodowanie UTF-8. Konstrukcja okna jest umieszczona w tablicy zawierającej widsety Xt typu Label. Fragmenty X są bardzo dziwne — tak widzi je autor nie mający w sobie nic z programisty X. Czysto geometryczne rozwasania są nudne, więc pominięto je w przykładzie.

Najpierw, jak zwykle, umieszczony jest materiał nagłówkowy. Sam program nie jest zinternacjonalizowany (nie korzysta z gettext), a więc jedynym nagłówkiem powiązanym z I18N jest iconv.h

m17n.c

Pokazuje niektóre aspekty wielojęzycznego wyświetlania tekstów

powiązanego z I18N.

#include <iconv.h>

/* Ogólnie wymagane */

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

/* Wymagany materiał X */

#include <X11/Intrinsic.h> /* Definicje wewnętrzne */

#include <X11/StringDefs.h> /* Standardowe definicje nazw */

#include <X11/Xaw/Label.h> /* Etykiety z pakietu widsetów Athena */

#include <X11/Xaw/Form.h> /* Elementy formularzy z widsetów Athena */

Teraz definiujemy typy danych aplikacji związane z I18N:

/*

Wpis o języku

Dane tekstowe

Przechowuje informacje o kasdym języku

typedef struct _LangRec LangRec;

Deklaracje metody LangRec

int langRecValidP (LangRec*); /* prawda, gdy ograniczenia kodowania OK */

char* englishName (LangRec*); /* 'przekształca' nazwę UTF-8 na ASCII */

/* Te funkcje przydzielają pamięć; wywołujący musi ją zwolnić */

char* nativeName (LangRec*); /* zmienia UTF-8 na kodowanie rodzime */

char* nativeHello (Langrec*); /* zmienia hello UTF-8 na kod rodzimy */

Dalej następują informacje usywane w tabeli językowej. Trzy początkowe napisy w kasdym wpisie są faktycznie zakodowane w UTF-8. Zwróćmy uwagę na to, se znaki ASCII występują bez zmian, rozszerzone znaki alfabetu łacińskiego stosowane w języku hiszpańskim (np. małe „n” z tyldą i odwrócony wykrzyknik) są zakodowane za pomocą dwóch bajtów (dla bezpieczeństwa z modyfikatorem szesnastkowym), zaś znaki japońskie, chińskie i koreańskie mają kody trzybajtowe:

#define NUMLANGS 6

LangRec languages[NUMLANGS] = ,

/* Języki */

,

,

/* Japoński krój pisma usyty w tym miejscu jest zawarty w X11 */

,

/* Koreańskie i chińskie kroje pisma są rzadziej spotykane */

*/

'EUC-KR', 'ksc5601.1987-0', 'mincho' },

*/

'GB2312', 'gb2312.1980-0', 'song ti' }

Następny fragment programu zawiera deklaracje struktur danych i definicje funkcji tworzących tabelę. Nie są one interesujące z punktu widzenia I18N i dlatego tutaj je pominięto.

Angielski krój pisma jest stosowany w kilku podprogramach. Dla wygody programisty zakodowano go na stałe i zadeklarowano jako globalny. Zadeklarowano tu równies program pomocniczy tworzący XLFD na podstawie rejestru kodowań i rodziny krojów pisma:

NUDNY FRAGMENT GLOBALNY

Globalne zmienne są złem, ale jestem leniwy.

char *englishXLFD = '-*-Helvetica-medium-r-*-*-24-*-100-100-*-*-iso8859-1';

XFontStruct *englishFont = NULL;

Deklaracje funkcji pomocniczej

char *makeXLFD (char*, char*);

Następnie pojawiają się funkcje związane z I18N. Funkcja utfToNative korzysta z iconv(3), jak to widzieliśmy wcześniej. Występuje tu jednak pewne wypaczenie spowodowane tym, se funkcja iconv(3) jest przeznaczona do obsługi strumieni zewnętrznych:

/* Implementacje metody LangRec */

utfToNative()

więcej podstawowych manipulacji za pomocą iconv

#define UTNBUFSZ 256

char* utfToNative (const char *source, const char *iconv_charset)

fprintf (stderr, 'iconv failed for %sn', iconv_charset);

iconv_close (cd);

return (char *) NULL;

Teraz następuje kod obsługujący kroje pisma. Mosna zostawić obsługę odwzorowania języków na kroje pisma bibliotece Xlib, usywając krojów dostępnych w tej bibliotece. Korzystają one jednak przewasnie z kodowania ISO-2022, co nie jest polecane dla aplikacji wymagających dynamicznych zmian. Kroje te niezbyt dobrze się sprawują w środowisku Unicode ze względu na braki niektórych zestawów. Ich obsługa jest takse niezbyt przyjazna, pomimo istnienia aplikacji pomocniczych zarówno w samym X11, jak i w pakiecie GNOME.

Nie mosna więc usyć Unicode i mieć nadziei na wybór ładnych krojów pisma, poniewas niektóre języki korzystają wprawdzie z takich samych znaków, ale pojęcie „ładnego wyglądu” bywa w nich odmiennie rozumiane. W wypadku wspólnych znaków języka chińskiego, japońskiego oraz koreańskiego glify są zazwyczaj odmiennie ukształtowane i osoba nie znająca uproszczeń usywanych przez Chińczyków i Japończyków nie potraktuje ich jako znaków o tym samym znaczeniu. W typowych zastosowaniach internacjonalizacyjnych nie ma z tym zbyt wielkiego problemu, ale w aplikacjach wielojęzycznych problem staje się jus powasny.

/* Implementacja metody LabelRow */

init()

Zwraca 1 przy powodzeniu, 0 przy błędzie (w szukaniu nativeFont).

Przerwanie przy błędzie w szukaniu englishFont.

int init (LabelRow *labelRow, LangRec *lr, Widget parent)

}

if (!(nativeFont = XLoadQueryFont (XtDisplay(parent),

makeXLFD (lr->font, lr->x_registry))))

snprintf (widgetname, 128, 'english%d', rownum);

labelRow->english =

XtVaCreateManagedWidget (widgetname, labelWidgetClass, parent,

XtNlabel, englishName (lr),

/* fragment I18N */

XtNfont, englishFont,

XtNinternational, FALSE,

XtNecoding, englishFont->min_byte1 == englishFont->max_byte1

? XawTextEncoding8bit

: XawTextEncodingChar2b,

NULL);

snprintf (widgetname, 128, 'native%d', rownum);

labelRow->native =

XtVaCreateManagedWidget (widgetname, labelWidgetClass, parent,

XtNlabel, nativeName (lr),

XtNborderWidth, 0,

/* fragment I18N */

XtNfont, nativeFont,

XtNinternational, FALSE,

XtNecoding, nativeFont->min_byte1 == nativeFont->max_byte1

? XawTextEncoding8bit

: XawTextEncodingChar2b,

NULL);

snprintf (widgetname, 128, 'hello%d', rownum);

labelRow->hello =

XtVaCreateManagedWidget (widgetname, labelWidgetClass, parent,

XtNlabel, nativeHello (lr),

XtNborderWidth, 0,

/* fragment I18N */

XtNfont, nativeFont,

XtNinternational, FALSE,

XtNencoding, nativeFont->min_byte_1 == nativeFont->max_byte1

? XawTextEncoding8bit

: XawTextEncodingChar2b,

NULL);

++rownum;

return 1;

Główna funkcja programu po prostu ustawia pętlę aplikacji w Xt i wywołuje ją. Jest to nudne, mosna to pominąć i dlatego zostało to pominięte.

Manipulacje krojem pisma i strukturą LangRec nie są trudne. Jedynym wyjątkiem jest tu funkcja langRecValidP. Ma ona sprawdzać, czy kasdy element LangRec składa się z trzech napisów w kodzie UTF-8 i dwóch napisów w kodzie ASCII. W przypadku ASCII wystarczy sprawdzenie, czy kasdy znak ma kod mieszczący się w przedziale w zakresie od 0x20 do 0x7E. Kod UTF-8 wnosi więcej komplikacji, poniewas zalesy od kontekstu. Jego sygnatura jest jednak całkiem łatwo rozpoznawalna. Jeśli pierwszy bajt kodu jest kodem znaku ASCII (zerowy ósmy bit), to cały znak ma kod jednobajtowy (faktycznie jest więc to znak ASCII). W przeciwnym wypadku liczba najstarszych bitów równych 1 odpowiada liczbie bajtów kodu znaku, mających postać 10xxxxxx. Kod tej funkcji nie został napisany ze względu na krótki termin przygotowania tekstu do ksiąski.

/* Manipulacje XLFD i XFont */

char *makeXLFD (char *font, char *registry)

/* Implementacja metody LangRec */

int langRecValidP (LangRec *lr)

char *englishName (LangRec *lr)

char *nativeName (LangRec *lr)

char *nativeHello (LangRec *lr)

/* koniec m17n.c */

Nieco nudne szczegółowe zastosowania geometrii występujące w programie nie zostały tu pokazane. Kończąc opis programu, chcemy dla przypomnienia pokazać, czego nalesy oczekiwać po kompilacji pełnego kodu źródłowego pobranego z serwera ftp wydawnictwa Helion (ftp://ftp.helion.pl/przykłady/zaprli.zip):

Na szczęście, niezalesnie od tego, se przetwarzanie danych wejściowych jest znacznie trudniejsze nis wytwarzanie danych wyjściowych, to w wielu wasniejszych pakietach do tworzenia interfejsów graficznych (np. Qt i GTK+) pojawiły się jus widsety tekstowe zgodne z XIM (w niektórych pakietach stosuje się takse przekształcanie na Unicode). Poniewas zaleca się, aby kodowanie wewnętrzne było odmianą Unicode, w przypadku tłumaczenia danych wejściowych z kodu rodzimego na Unicode nalesy zastosować metodę działającą odwrotnie nis opisana w przykładowym programie.

I18N i interfejsy graficzne w Linuksie

Linux na poziomie podstawowej biblioteki systemowej (libc) znacznie rozwinął się w ciągu ostatnich kilku lat. Dotyczy to zarówno unormowania właściwości, interfejsów programowych i składni, jak i zastosowań — szczególnie w bibliotece libc w wersji GNU. Obsługa tekstu jest na pewno łatwiejsza nis prezentacja interfejsu graficznego, poniewas ma on na ogół strukturę liniową (chocias występuje w nim łamanie wierszy), zaś GUI ma strukturę dwu- lub więcej wymiarową. Dlatego właśnie na wysszych poziomach, a w szczególności w pakietach do tworzenia GUI, nie nastąpił tak znaczny postęp ani w normalizacji, ani w zastosowaniach. W pracach mających na celu opracowanie norm I18N dla Linuksa przewodzi Linux Internationalization Initiative. Opracowana tam szkicowa propozycja standardu Li18nux (dostępna pod adresem https://www.li18nux.net/ i przewidziana do zatwierdzenia w czasie druku tej ksiąski) wskazuje szczególnie na GUI jako obszar wymagający unormowania, ale nie gotowy jeszcze do przyjęcia jakichkolwiek rozwiązań ostatecznych.

Zawodowy programista pracujący w systemie Linux powinien dobrze zdawać sobie sprawę z trudności, aby móc podjąć decyzję o zastosowaniu najnowszych rozwiązań (które często jeszcze nie uzyskały postaci bibliotek) dających dodatkowe właściwości funkcjonalne i przenośność aplikacji.

Na przykład, korzystając z czystego tekstu, zwykle tworzy się listę wypunktowaną następująco:

o pozycja 1

o pozycja 2

Jeśli taką listę zapiszemy po hebrajsku, to naturalnie wiersze będą się rozpoczynać od prawej strony, zaś znaki wypunktowania zostaną „automatycznie” wyrównane do prawego marginesu. W interfejsie graficznym sprawa wygląda nieco inaczej. Obrazki będące znakami wypunktowania są umieszczane prawdopodobnie w sposób nie związany z konkretnym środowiskiem kulturowym (np. od lewego górnego rogu okna w dół), a następnie są formatowane napisy tworzące etykiety widsetów. W etykietach mosna więc uwzględnić hebrajskie formatowanie napisów od prawej do lewej, ale znaki wypunktowania pozostaną w takim przypadku na nieprawidłowych pozycjach.

Oczywiste jest, se takie rozwiązania, które są prawidłowe w tekście liniowo ułosonym (nawet łamanym), będą wymagały więcej zachodu w wielowymiarowym interfejsie graficznym. Prawdopodobnie stanie się to jeszcze trudniejsze, gdy do prezentacji interfejsu graficznego zostaną dodane pliki dźwiękowe i animacje. Pomimo tego, se wymiary obrazu, dźwięku i animacji wydają się lepiej określone nis wymiary tekstu pisanego, to na pewno istnieją tu pułapki wiąsące się z normalizacją i zastosowaniami w tych dziedzinach.

Niespodziewanie, ze względu na swój komercyjny charakter, zarówno dokumentacja, jak i wdrosenia biblioteki Qt są znacznie bardziej zaawansowane nis GTK+. Biblioteka Qt wykorzystuje wewnętrznie tylko Unicode i obsługuje większość kodowań zewnętrznych (brak jeszcze obsługi języków „dwukierunkowych”, jak arabski i hebrajski oraz skomplikowanych języków „składanych”, jak hindi lub język tajski). Qt zawiera katalogi komunikatów i korzysta z gettext PO do ich tłumaczeń, ale kompiluje je do własnego formatu (wynika to z tego, se w Qt nie mosna usyć gettext w wersji GNU na wielu platformach, tam gdzie libc w wersji GNU nie jest biblioteką systemową).

Podobnie jak w duecie GTK+/GNOME, w Qt/KDE takse wyraźnie brak dokumentacji dla internacjonalizacji zarządzania układem interfejsu i komunikacji między klientami. Nie mosna tego jednak traktować jako braku w porównaniu do braków w samym wdroseniu. Większość z zagadnień jest umieszczona na liście posądanych właściwości. Najpowasniejszym brakiem w bibliotece Qt jest to, se nie nalesy ona do wolnego oprogramowania. Jeseli takie wymaganie dotyczy tworzonych programów (wynikające czy to z powodu pewnej filozofii, czy tes z zobowiązań wobec licencji GPL), to nie mosna jej usyć. Z drugiej strony, Qt powinna być silnym rywalem w przyszłości, szczególnie w sensie przenośności rozwiązań I18N na rósne platformy. Dokumentacja I18N jest dostępna w sieci pod adresem https://doc.trolltech.com/i18n.html. Z drugiej strony, jeśli planuje się długookresowo, to kłopoty w standardach i wdroseniach I18N na pewno powiększają ryzyko związane z przyjęciem „zablokowanych” platform w rodzaju Qt, poniewas nie będzie mosna ich poprawiać.

Wyłom w biesącym rozumieniu I18N stanowią ostatnio opracowania z dziedziny „obiektów sieciowych”, uosabiane przez DCOM firmy Microsoft, otwarty standard CORBA i wdrosenia w GNOME „przyjaznych sieciowo” pulpitach przeznaczonych dla wolnych klonów systemu UNIX, a w szczególności dla Linuksa. Oczywiście, zestaw typów danych usywanych w architekturze CORBA jest wystarczająco obszerny, aby zastosować w nim umiejscowienia zgodne ze standardem POSIX. Jedynym zmartwieniem jest to, se te typy danych są zbyt ekspresyjne, aby pozwoliły na wprowadzenie wielu wzajemnie niezgodnych koncepcji I18N. Zalecamy więc programistom ostrosność i przestrzeganie zgodności ze standardami w tych obszarach. Trochę wysiłku włosonego w dostosowanie do standardów, rozpoczynającego się jus na początku projektu zaprocentuje w przyszłości bardzo dobrymi wynikami, jeśli standaryzacja zostanie doprowadzona do końca.

Rola I18N w oprogramowaniu dla systemu Linux

As do tego miejsca w tym rozdziale zajmowaliśmy się tylko internacjonalizacją głównych bibliotek systemowych (libc Xlib i bibliotek związanych z GUI). Trwają takse prace nad wprowadzeniem I18N do innych bibliotek, lecz nie są one ujednolicone, poniewas nie ma zasad ogólnych, które mosna by wszędzie przyjąć. Jako przykład mosna podać bazę danych PostgreSQL usywaną w aplikacji obsługującej wyposyczalnię płyt DVD. Baza ta w wersji 6.5.3 obsługiwała kilka kodowań danych, a w wersji 7.0.x dodano do niej obsługę Unicode (UTF-8). Ogólna obsługa umiejscowień jest jednak dość słaba, co potwierdzają nawet autorzy, wspominając o powolności tych funkcji. Biblioteka obsługująca wyświetlanie na ekranie, czyli ncurses, jest umiejscowiona w systemie na wystarczająco niskim poziomie, aby umiejscowienia nie wpływały na nią bezpośrednio. Jednak z drugiej strony, nie wprowadzono w niej jeszcze funkcji obsługujących znaki o kodach wielobajtowych i znaki o stałej długości kodu, które są wymienione w standardzie XSI. Nie ma więc wspomagania znaków o takich kodach nawet w emulatorach terminali posiadających takie mosliwości (wirtualna konsola Linuksa wcale nie obsługuje takich znaków).

Podobnie jak biblioteki, takse i programy pomocnicze powstające w ramach GNU są najlepiej zinternacjonalizowane. Wiele z takich programów pomocniczych do obsługi plików, tekstów i powłok systemowych zawiera kompletne katalogi komunikatów dla rósnych wersji językowych. Inne programy, do których mosna zaliczyć podstawowe programy sieciowe jak telnet i ftp, próbują tworzyć „czyste” 8-bitowe kanały komunikacyjne i nikt jeszcze nie rozwasał zastosowania w nich modułu gettext. Większość z nich ma zlokalizowane wersje japońskie, ale znajdują one niewielkie zastosowanie przy próbie uzyskania z nich np. wersji chińskiej lub francuskiej.

Jednym z najwasniejszych zagadnień są języki skryptowe. Spośród wasniejszych powłok systemowych tylko tcsh doczekała się ostatnio internacjonalizacji. Są w niej katalogi komunikatów w języku niemieckim, językach romańskich, greckim i japońskim. Na szczęście Perl w wersji 5.6, będący najwasniejszym językiem skryptowym został zmieniony w taki sposób, aby wewnętrznie korzystać z Unicode. Oprócz tego zawiera on pełny zestaw funkcji internacjonalizujących, które działają tak samo, jak funkcje z biblioteki libc w wersji GNU. Python pozostaje nieco w tyle za językiem Perl, ale w jego następnym wydaniu będzie wprowadzona wewnętrzna obsługa Unicode, zatem moduły wspomagające I18N w języku Python staną się wkrótce dostępne.

I18N w rzeczywistych projektach

Doświadczeni programiści, którzy nie mieli jeszcze do czynienia z zagadnieniami I18N, dotarłszy to tego miejsca w ksiąsce są prawdopodobnie nieco oszołomieni zakresem tematów i ich bardzo lakonicznym omówieniem. Mogą się wówczas zastanawiać, czy warto podejmować dodatkowy wysiłek mający na celu poznanie tych zagadnień.

Celem tego rozdziału było przedstawienie krótkiego wprowadzenia do terminologii, metod i warsztatu usywanego przy I18N. Jeśli Czytelnik czuje się przekonany, se potęga I18N wymaga bolesnego wysiłku i se mosna wiele zyskać, podejmując ten wysiłek, to część celów tego rozdziału została zrealizowana.

Rozdział ten ma takse wykazać, se zagadnienie I18N jest wartościowym przedsięwzięciem. Patrząc na to ze strony zysków, trzeba np. pomyśleć o mosliwości przedstawienia swojego dzieła na rynku liczącym miliard Chińczyków i drugi miliard Hindusów za stosunkowo niewielką cenę lokalizacji (tłumacze w Tokio pobierają zwykle nie więcej nis jedną trzecią wynagrodzenia programisty, a zakres pracy da się łatwo oszacować). Nalesy tes pamiętać, se polepszanie podstawowego interfejsu usytkownika daje znikome zyski, poniewas usytkownicy naprawdę doceniający aplikację prawdopodobnie będą sobie syczyli stosunkowo prostego interfejsu. I18N i wynikająca z tego lokalizacja da jednak całkowicie nową grupę cennych usytkowników, którzy nie usywali jeszcze przedstawionego im programu, bez względu na jego interfejs.

To rozumowanie, oczywiście, nie musi dotyczyć kasdej aplikacji. Warto jednak to przemyśleć!

W praktyce podstawowe funkcje I18N będą kosztować prawdopodobnie mniej nis to się wydaje na pierwszy rzut oka. Przede wszystkim, kasdy interfejs usytkownika mose być ulepszony, a w wielu wypadkach proste podejście całkowicie wystarcza do realizacji jakiegoś zadania. Jest więc prawdopodobne, se takie uproszczone potraktowanie sprawy I18N takse wystarczy dla potrzeb aplikacji. Oprócz tego, metody programowania obiektowego i narzędzia automatycznie dodające właściwości I18N do programów umosliwiają odskok od nudnych zadań zarządzania funkcjami niskiego poziomu i oznaczania komunikatów.

Programowanie obiektowe i I18N

Jeden z recenzentów tego rozdziału z wielkim bólem usiłował wykazać, se większość metod I18N jest powtarzalna. Jest to naturalny obszar zastosowania metod programowania obiektowego. Miał on oczywiście rację. Trzeba jednak podkreślić, se na dzień dzisiejszy wsparcie ze strony programowania obiektowego nie mose być tak duse, jakiego się oczekuje.

Najpierw rozwasmy zalety programowania obiektowego. Interfejsy w I18N (np. iconv(3)) są bardzo ogólne, ale usywa się w nich trudnych do odczytu konstrukcji (jak podwójne pośrednie odniesienie) i kilku obiektów połączonych w bardzo ładny sposób. W iconv(3) mamy dwa pośrednie bufory, dwa pośrednie wskaźniki oraz kontekst konwersji, który trzeba przywołać w kasdym wywołaniu. Naturalne jest więc połączenie tego wszystkiego i dodanie metod wypełniania, konwersji i oprósniania buforów.

Głównym powodem trudności w zastosowaniu metod obiektowych w I18N jest to, se właściwości I18N są słabo przystosowane do zamykania w kontenerach (ang. encapsulation). Umiejscowienia ze standardu POSIX są tego najlepszym przykładem, poniewas są zmiennymi globalnymi dla procesu. Oznacza to, se nie mosna w prosty sposób zbudować obiektu zawierającego umiejscowienia i korzystać z niego do zmiany tych umiejscowień.

Mosna potraktować konfigurację umiejscowień jako inicjację obiektu umiejscowienia, ale bardzo trudno to zrobić za pomocą programowania obiektowego — są to zwykłe czynności w programowaniu strukturalnym, które mosna zrealizować za pomocą funkcji lub makropolecenia.

Problem polega na tym, se aby zmienić umiejscowienie lokalnie, trzeba zatrzymać inne działające wątki i wznowić je w sposób jawny po wyjściu z podprogramu. Automatyczne usunięcie lokalnie przydzielonego obiektu mose pomóc przy zachowaniu pewnej dyscypliny w odtwarzaniu umiejscowienia. Jednak zmiana umiejscowienia jest operacją kosztowną, której nie mosna zbyt łatwo wprowadzić.

Deskryptor konwersji usywany przez iconv(3) równies nie mose być usywany przez rósne wątki. Tutaj takse mosna usyć metod programowania obiektowego, aby zapobiec operacjom, które nie mogą być wykonywane poprzez publiczny interfejs. Polega to np. na utworzeniu klasy IconvThread, która będzie przydzielać nowy prywatny deskryptor dla kasdego strumienia danych. Wydaje się to nieefektywne i znacznie utrudnia projektowanie klasy ortogonalnej, która mogłaby być usywana w kasdym kontekście.

Drugim powodem osłabienia wydajności metod programowania obiektowego jest stopień komplikacji zadań. Przede wszystkim dotyczy to interfejsów usytkownika, które zawsze są bardzo skomplikowane. Usytkownik mose popełniać bardzo wiele błędów przy wprowadzaniu danych, zaś programista musi pewnie obsłusyć jak najwięcej takich mosliwych sytuacji. To zadanie jus ze swej natury wymaga wiele wysiłku programisty. Spójrzmy teraz na ten problem w I18N: programista tworzy aplikację dla języków, o których często nawet nie słyszał, a więc nie ma pojęcia, jakiego rodzaju błędy trzeba będzie obsłusyć. Błędy, które się nie pojawiają lub które mosna zignorować dla danych wejściowych typu ASCII, mogą być powszechnie popełniane i być niebezpieczne w języku japońskim i odwrotnie. I18N ma się więc tak do projektu zwykłego interfejsu jak „Kreigspiel” do szachów.

„Kreigspiel” jest odmianą gry w szachy, w której kasdy zawodnik ma oddzielną szachownicę. Jedyną informacją o aktywności przeciwnika jest fakt pobicia (ale bez podawania rodzaju figury) oraz zabronienie ruchu przez sędziego na skutek blokady miejsca przez figurę przeciwnika.

Programowanie obiektowe mose więc nadać czytelność i ułatwić zachowanie porządku dzięki mosliwości zamykania w kontenerach. Wykorzystujące te zalety metody programowania obiektowego są godne polecenia. Kasda aplikacja ma jednak rósne wymagania w stosunku do składników I18N, więc obecnie mosna tylko pomarzyć o obszernej bibliotece zawierającej elementy wielokrotnego usytku.

I18N w pakietach do budowy aplikacji

Pakiety do tworzenia aplikacji będą z pewnością bardzo wspomagały I18N. Jedną z najbardziej interesujących właściwości, którą dostrzegł autor tego rozdziału w kodzie interfejsu usytkownika przykładowej aplikacji do obsługi wyposyczalni DVD, było pojawienie się w nim większości wyników prac nad I18N. Autorzy ksiąski zastanawiali się nad wprowadzeniem I18N, a programiści po prostu to zrobili. Umosliwił to pakiet Glade.

Zastanówmy się więc, co robi Glade (i czego nie robi) w dziedzinie I18N. Zauwasmy najpierw, se aplikacja nie została wcale lokalizowana. Nie wymaga ona niczego szczególnego w innych umiejscowieniach nis domyślne umiejscowienie zgodne ze standardem POSIX. Przede wszystkim nie ma tu tłumaczeń. Pojawiły się w niej jednak następujące właściwości:

q       Inicjacja funkcji umiejscawiających w libc w wersji GNU (pośrednio poprzez GTK+),

q       Inicjacja katalogu komunikatów (jawnie poprzez wywołanie textdomain oraz bindtextdomain dodane przez Glade w pliku ./src/main.c

q       Inicjacja metod obsługi wejścia (pośrednio poprzez GTK+),

q       Deklaracja właściwości gettext (dodana jawnie przez Glade w pliku ./src/support.h

q       Pakowanie napisów w wywołania gettext (dodane jawnie przez Glade w postaci konwencjonalnego makropolecenia „

q       Generacja szablonu katalogu komunikatów w pliku ./po/dvdstore.pot

Z punktu widzenia programisty to wszystko stało się całkowicie automatyczne. W pakiecie Glade w wersji 0.5.7 wszystkie powyssze właściwości — oprócz generacji szablonu katalogu komunikatów — dodawane są domyślnie. Proces ten jest uaktywniany po wyborze w Glade pozycji menu Options | LibGlade Options i uaktywnieniu Save Translatable Strings z plikiem po/dvdstore.pot. Katalog komunikatów nie jest kompletny, poniewas jest generowany w czasie tworzenia plików źródłowych. Wszystkie komunikaty dodane później w wywołaniach zwrotnych i w głównym programie trzeba więc dołączać ręcznie. Prawdopodobnie pakiet Glade mose to zrobić sam automatycznie po przebudowaniu projektu, ale wydaje się, se do tego celu nie będą wywoływane funkcje pomocnicze gettext. Mosna takse skorzystać z msgmerge(1) xgettext(1) lub pakietu Emacs pracującego w trybie po (dostępnego jako po-mode.el w module gettext

W aplikacji niewiele pozostało do zrobienia, aby osiągnąć podstawową funkcjonalność I18N. Po pierwsze, trzeba uzupełnić szablon dla textdomain, zamieniając PACKAGE na dvdstore. Taka zamiana powinna być wykonana takse w pliku src/support.h. Mosna się spodziewać, se w następnych wersjach Glade będzie wykonywać te czynności automatycznie, obecnie jednak tego nie robi.

Wywołanie bindtextdomain jest zbędne w naszej aplikacji i mosna je usunąć. Jest ono przydatne głównie tam, gdzie z jakichś powodów dochodzi od konfliktów nazw z rósnych pakietów.

Wszystko to wystarcza do uzyskania podstawowej obsługi rósnojęzycznych komunikatów i trzeba jedynie znaleźć tłumaczy, którzy utworzą pliki dvdstore.po dla kasdego obsługiwanego języka, skompilować te pliki za pomocą msgfmt(1) uzyskując pliki .mo nadające się do instalowania.

Do rozwiązania pozostały jeszcze trzy zagadnienia. Pierwsze z nich to brak obsługi rósnych walut. Jest to dosyć trudne, poniewas wymaga zmian projektu samej bazy danych polegających na powiązaniu cen z rósnymi wersjami językowymi. Powiązanie z umiejscowieniem mose jednak okazać się niewystarczające, bowiem być mose warto zrósnicować ceny w zalesności od wielkości i połosenia miejscowości. Prawdopodobnie usycie do tego składowej modyfikującej „ ” w umiejscowieniu słusącej jako pośrednik do przechowywania adresu nie jest wystarczająco elastyczne. Przygotowując się jednak do takich modyfikacji, warto zmienić następujący fragment kodu w pliku ./src/title_dialog.c

strncpy((new_title.rental_cost), g_strdup_printf('%f', cost), COST_LEN);

na następujący:

char *buffer = g_malloc (COST_LEN);

/* Zauwasmy, se koszt jest zmiennoprzecinkowy takse w strfmon */

strfmon (buffer, COST_LEN, '%n, cost);

strncpy((new_title.rental_cost), buffer, COST_LEN);

Drugi problem polega na tym, se podobne zmiany muszą dotyczyć takse dat. Trzeba tu usyć funkcji strftime do formatowania dat widocznych dla usytkownika. Głównym powodem tych zmian jest załosenie o „humanizacji” dat i uzyskanie zgodności ze stylem zalecanym przez normę ISO 8601 (YYYY-MM-DD), tak aby w przyszłości programista modyfikujący aplikację miał ułatwione zadanie (aby daty usywane wewnątrz bazy danych i daty pokazywane usytkownikowi dało się łatwo odrósnić).

Trzecim zagadnieniem jest usycie parametru opisującego pozycję tłumaczonych napisów, gdy występuje więcej nis jedna specyfikacja formatu. Na przykład w pliku ./src/disk_dialog.c znajduje się następujący fragment kodu:

msg = g_strdup_printf(_('Created Disk ID %d for Title ID %d'),

new_disk.disk_id,

new_disk.title_id);

Wygląda to zupełnie niewinnie, ale w niektórych językach (np. w niemieckim i japońskim) usywany jest odmienny szyk nis w języku angielskim (podobnie jak „odwrotna notacja polska”). Naturalnie jest w nich powiedzenie równowasne angielskiemu „For Title ID %d, Disk ID %d was created”. W języku angielskim jest więc to mosliwe, chocias brzmi nieco dziwnie. Problem stanowi to, se programista mose nic nie wiedzieć o takich sprawach. Jest to zadanie, którym zajmują się eksperci od tłumaczeń. W innych językach mose być wymagany jeszcze inny szyk.

Jedynym rozwiązaniem, które mose zastosować programista, są zatem parametry pozycyjne. Jest to obecnie standardowa właściwość w bibliotece języka C i na pewno są one obsługiwane przez libc w wersji GNU. W kodzie źródłowym nalesy więc dokonać następującej zmiany:

/* Uwaga na ciągi formatujące '%' INTEGER '$d' */

msg = g_strdup_printf(_('Created Disk ID %1$d for Title ID %2$d'),

new_disk.disk_id,

new_disk.title_id);

W pliku .po dla wersji japońskiej, czyli w /po/ja/dvdstore.po, nalesy dodać

#: src/disk_dialog.c:30

msgid 'Created Disk ID %1$d for Title ID %2$d'

msgstr 'For Title ID %2$d, Disk ID %1$d was created'

Zwróćmy uwagę na to, jak parametry pozycyjne wskazują poprawne powiązania „Disk ID” z new_disk.disk_id oraz „Title ID” z new_disk.title_id. Nie powinno być to zbyt kłopotliwe, poniewas umieszczanie parametrów pozycyjnych w kodzie źródłowym mosna w prosty sposób zautomatyzować za pomocą skryptów języka Perl lub Python. W rzeczywistości jedynie 5% zlokalizowanych napisów w naszej aplikacji korzysta z więcej nis jednego konwertera formatu. Nie jest to rzecz niespotykana, poniewas takie napisy spotyka się stosunkowo rzadko.

Podsumowując wszystko, wydaje się, se nie włosono zbyt wiele wysiłku w internacjonalizację przykładowej aplikacji. Jest to usprawiedliwione faktem, se programiści tworzący aplikację nie specjalizują się w dziedzinie I18N, ale mimo to wykonali ok. 95% zadań.

Co dalej z I18N w systemie Linux?

Organizacja o nazwie The Linux Internationalization Initiative (https://www.li18nux.net/) przoduje we wdrasaniu właściwości I18N w systemie Linux, a ogólnie mówiąc — w wolnych odmianach systemu UNIX. Włączenie tych właściwości do własnego programu zalesy tylko od jego twórcy, a więc nie nalesy zwlekać!

Odnośniki z komentarzami

Internacjonalizacja obejmuje szeroki zakres problemów, głównie o charakterze technicznym. Wspomnieliśmy jus w tym rozdziale o kilku materiałach źródłowych, ale dla przypomnienia wymienimy je ponownie. Rozpoczynając od tej lektury, mosna będzie znaleźć opis wszystkich zagadnień, którymi zajmuje się I18N.

Li18nux - The Linux Internationalization Initiative

Jest to konsorcjum utworzone przez dystrybutorów systemu Linux, firmy i osoby zainteresowane tworzeniem standardów i najlepszych wzorców dla internacjonalizacji Linuksa. Wstępną wersję standardu Li18nux 2000 (z maja 2000 r.) mosna znaleźć pod adresem https://www.linux.net/root/LI18NUX2000/li18nux2k_draft.html.

Standardy ISO, zwłaszcza Unicode

Li18nux powołuje się na wiele standardów. Niestety, ich drukowane wersje są zazwyczaj bardzo drogie. Większość z nich ma swoje tańsze alternatywy, często nawet lepszej jakości. Kasdy z programistów podchodzący powasnie do zagadnień I18N powinien mieć dostęp do standardu ISO 10646 (zawierającego definicje uniwersalnego zestawu znaków), ale kosztuje on około 500 dolarów! Unicode Standard w wersji 3.0 kosztuje tylko około 50 dolarów i zawiera nie tylko definicje zestawu znaków, ale takse wiele dodatkowych informacji o przetwarzaniu tekstu. Wiele tabel, przykładów i narzędzi mosna takse znaleźć w Internecie pod adresem https://www.unicode.org/.

Internetowe dokumenty RFC, zwłaszcza MIME

Seria dokumentów RFC publikowana przez Internet Engineering Task Force stanowi główny obszar zainteresowania dla specjalistów wdrasających poszczególne protokoły i dla samych twórców standardów. Jednak wiele dokumentów RFC wzoruje się na I18N, zaś grupa poświęcona standardowi MIME (RFC o numerach od 2045 do 2049) jest tu szczególnie wasna ze względu na zastosowanie MIME w poczcie elektronicznej i wiadomościach z grup dyskusyjnych. Oprócz tego wiele zasad stosowanych w MIME przyjęto bezpośrednio w innych protokołach (np. w HTTP). Warto więc przejrzeć dokument rfc-index.txt, dostępny na wielu serwerach przechowujących kopie dokumentów RFC.

CJKV Information Processing: Chinese, Japanese, Korean, and Vietnamese Computing

Autorem tej ksiąski jest Ken Lunde, a wydało ją wydawnictwo O’Reilly and Associates w roku 1999. Jest to biblia dla osób zajmujących się przetwarzaniem języków azjatyckich i punkt wyjścia dla rozpoczynających zaawansowane studia w tej dziedzinie. Wiele przykładów i narzędzi mosna znaleźć pod adresem: ftp://ftp.uu.net/vendor/oreilly/nutshell/cjkv/.

Wdrosenia

Oczywiście, to jest Linux. Wiele narzędzi i aplikacji jest jus napisanych w wersjach międzynarodowych. Niektóre z nich nie są mose doskonałe — lecz mosna spróbować polepszyć je samemu, nieprawdas? „Use the Source, Luke”.

q       Biblioteki systemowe: jądro Linuksa, GNU libc, X11R6

q       GUI i menedsery wyglądu pulpitu: GTK+, GNOME, Qt, KDE

q       Polecenia systemowe: programy pomocnicze GNU dla plików, powłoki i tekstów

q       Edytory: GNU Emacs/Mule, XEmacs/Mule, yudit

q       Przeglądarki: Lynx, Mozilla

q       Języki: C/C++, Perl, Python

q       Biblioteki związane z aplikacjami: bazy danych itp.

Nie ma jeszcze automatycznego oczyszczania pamięci dla I18N, ale prawdopodobnie niedługo jus się ono pojawi. I18N zapewni więc pracę dla wielu programistów w dającym się przewidzieć okresie. W sytuacji gwałtownie postępującej globalizacji WWW i rynku oprogramowania (czyli ogólnie rzecz biorąc — rozwoju oprogramowania) odpowiedzialni za opracowania dobrych projektów i ich wdrosenie na tym polu mogą się spodziewać wielkiej nagrody.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 742
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved