R-28-07.doc

(480 KB) Pobierz
PLP_Rozdział_28

Rozdział 28. Wersje międzynarodowe

Jednym z najważniejszych aspektów pracy profesjonalnego programisty jest gotowość do podjęcia odpowiedzialności za tworzenie aplikacji wyposażonych we wszelkie funkcje wymagane przez użytkowników. W dzisiejszej globalnej gospodarce ta pełna funkcjonalność często oznacza, że 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 można uniknąć, stosując format nie pozwalający na dowolność interpretacji, czyli używając np. pełnych nazw miesięcy i czterocyfrowego zapisu roku.

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

Załóżmy, że 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 możliwość poprawnego sortowania. Najważniejszym 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 ważne poza Ameryką Północną i północną Europą (nawet w takich krajach jak Japonia), gdzie klienci podczas przekształcania wprowadzanych danych na format używany wewnątrz firmy wymagają pomocy osoby znającej zagadnienie.

Istotnie, mając wielką liczbę używanych powszechnie formatów dat, nie ma możliwoś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 użytkownika, 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 wdrożone 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 niż powszechna praktyka środowiska programistów używają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 http://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 http://www.linux.net/root/LI18NUX2000/li18nux2k_draft.html) zawiera odnośniki do wszystkich standardów wiążących się z tymi zagadnieniami oraz liczne przykłady ich użycia. Tekst tego dokumentu jest bardzo lakoniczny, a więc nie polecamy jego studiowania. Należy go traktować jako zbiór pomocnych odnośników, ponieważ 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 można 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 można 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, że standardowe właściwości pojawiły się całkiem niedawno. Ich zastosowania nie są jeszcze wystarczająco stabilne (co można 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 że 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 może być ustawianie widżetów w graficznym interfejsie użytkownika umożliwiające obsługę odmiennego kierunku czytania w językach takich jak hebrajski lub arabski). Należy mieć nadzieję, że ten rozdział zachęci Czytelników do pionierskich prac w tej dziedzinie i do tworzenia wzorców dla zwykłych programistów, a także 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). Można 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 możliwość bardziej efektywnej adaptacji, jeśli o tych sprawach będzie się pamiętać już od pierwszej wersji programu.

Można też postępować inaczej, stosując coś, co jest nazywane internacjonalizacją (ang. internationalization). Internacjonalizacja programu oznacza użycie 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, że przy lokalizacji programu może być konieczne wprowadzenie dużych zmian w kodzie, ale przy odpowiedniej internacjonalizacji obsługa wszystkich różnic językowych będzie zapewniona dzięki załadowaniu odpowiednich plików danych. Zmniejsza to nie tylko nakład pracy programisty, ale także ze znacznie większym prawdopodobieństwem da poprawny wynik. Wynika to np. z faktu, że tłumaczeniami zajmują się specjaliści znający dany język, a nie programiści.

Warto pamiętać, że 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 używany przy tłumaczeniach, ale także 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, że działająca w architekturze klient-serwer aplikacja dla wypożyczalni płyt DVD może działać w środowisku wielojęzycznym (tzn. użytkownicy 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 wypożyczalni 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, że skutecznie przyporządkowuje unikatowy kod każdemu znakowi użytemu 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 umożliwienie reprezentacji wszelkich tekstów we wszystkich językach świata. Jest to także standard opisujący kodowanie znaków w postaci odwzorowania zestawu znaków na zbiór liczb całkowitych. Bieżą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 także opis niektórych właściwości znaków, np. wartości liczbowe cyfr oraz opis standardowych algorytmów używanych przy przetwarzaniu znaków (np. przy sortowaniu).

Zestaw znaków jest zbiorem uporządkowanym. Znaki mają różne właściwości, np. glify używane 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 (zauważmy, że w Unicode unika się mówienia o liczbach całkowitych).

Przeważnie długość ciągu kodu bitowego przyporządkowanego znakowi jest wielokrotnością liczby 8. We współczesnych komputerach pokrywa się to z bajtem. Ponieważ może się kiedyś zdarzyć, że bajt będzie miał różną długość w komputerach różnego typu, to dla oznaczenia ciągu ośmiu bitów wprowadzono określenie „oktet”. Jeżeli 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 używa 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 używane 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, ponieważ znaki są przeważnie porządkowane w bloki wyrównane w „wierszach” liczących 256 pozycji kodu każdy. Każdy 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 każdy tekst można było przetłumaczyć bez zniekształceń, używając jednego uniwersalnego kodowania. Ponieważ 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 już o hieroglifach, to w 16-bitowej przestrzeni kodowej nie ma już miejsca na 26 liter alfabetu łacińskiego, na alfabet grecki, cyrylicę i symbole matematyczne. Projektanci UCS zdawali sobie sprawę, że trzeba będzie więcej niż 16 bitów do zakodowania wszystkich potrzebnych znaków (nawet przy założeniu 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, że 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 zbliżony do zestawu znaków Unicode, ponieważ 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 umożliwi 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, że 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, że liczba znaków w tabeli nie jest taka sama jak liczba znaków Unicode wyrażona w reprezentujących ją ciągach bitowych) doprowadziły do zdefiniowania „płaskiego” formatu UTF-32. Użyto w nim znaków o kodach 32-bitowych, lecz ich liczba jest dokładnie taka, jak w formacie UTF-16, ponieważ 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ć żadnych znaków kodom większym od 1114112.

Wszystko doszło więc do etapu, gdy istnieje jeden niepodważalny 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 wyżej wymienionych formatów istnieje znany format 8-bitowy UTF-8, charakteryzujący się tym, że wszystkie znaki ASCII są w nim przedstawiane jako kody jednobajtowe, zaś pozostałe są kodowane za pomocą więcej niż jednego bajtu (każdy z tych bajtów ma wartość z zakresu od 0x80 do 0xFF). Oznacza to, że 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ą także inne przekształcenia formatu, ale obecnie można 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 (zależnie od kontekstu „Unicode” może 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 niż są reprezentowane jako pary znaków zastępczych (pierwszy o kodzie od 0xD800 do 0xDBFF, a drugi o kodzie od 0xDC00 do 0xDFFF).

Pożądany, ale nie wymagany bezwzględnie w aplikacjach korzystających z semantyki „napis jest tablicą znakową”, w których musi być dostępna możliwość przesyłania całego zakresu znaków Unicode, wymaga się większej zwartości niż 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 możliwość przesyłania całego zakresu znaków Unicode, wymaga się większej zwartości niż 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 także efektywny sposób kodowania dla aplikacji, w których oczekuje się dużej części danych tekstowych typu ...

Zgłoś jeśli naruszono regulamin