Wprowadzenie
Zend_Locale to odpowiedź frameworka na pytanie: "Jak jedna aplikacja
może być używana na całym świecie?". Większość odpowie: "To proste. Wystarczy przetłumaczyć
wszystkie napisy na inne języki.". Jednak użycie prostych tabel mapujących frazy jednego
języka na drugi nie jest wystarczające. Różne regiony mogą mieć różne konwencje dotyczące
imion, nazwisk, zwrotów grzecznościowych, formatu liczb, dat, czasu, waluty itp.
Niezbędna jest
Lokalizacja
i internacjonalizacja. Zwroty te często są przedstawiane skrótowo jako odpowiednio
L10n oraz I18n. Internacjonalizacja zakłada
przystosowanie do użycia systemu niezależnie od specjalnych wymogów
charakterystycznych dla użytkowników
w zakresie języka, regionu, sposobu zapisu liczb, konwencji finansowych, dat i czasu.
Lokalizacja określa dodanie do systemu funkcjonalności obsługujących określone wymogi
dotyczące języka, konwencji dat, czasu, waluty, nazw, symboli, sortowania itp.
L10n i I18n uzupełniają się nawzajem. Zend Framework udostępnia
ich obsługę poprzez szereg komponentów. M. in.:
Zend_Locale, Zend_Date,
Zend_Measure, Zend_Translate,
Zend_Currency, and Zend_TimeSync.
Zend_Locale i setlocale()
W dokumentacji PHP można przeczytać, że
funkcja setlocale() nie jest bezpieczna wątkowo (thread-safe)
ponieważ działa w zasięgu procesu a nie wątku. To oznacza, że w środowisku
wielowątkowym może dojść do sytuacji w której locale ulegnie zmianie pomimo braku
odwołań do funkcji setlocale() w skrypcie.
To, z kolei, może prowadzić do nieoczekiwanych rezultatów działania programu.
Podczas używania Zend_Locale takie ograniczenia nie występują
ponieważ klasa Zend_Locale jest całkowicie niezależna od funkcji
PHP setlocale().
Co to jest lokalizacja
Lokalizacja oznacza, że aplikacja (lub strona) może być używana przez różnych
użytkowników, którzy mówią w różnych językach. Jednak przetłumaczenie napisów
to tylko jedno z wielu zagadnień z tym związanych. W Zend Framework składają
się na nią:
Zend_Locale - Główna klasa wspierająca locale dla
pozostałych komponentów Zend Framework.
Zend_Translate - Tłumaczenie łańcuchów znaków.
Zend_Date - Lokalizacja dat, czasów.
Zend_Calendar - Lokalizacja kalendarzy (ze wsparciem
dla systemów kalendarzy innych niż Gregoriański)
Zend_Currency - Lokalizacja walut.
Zend_Locale_Format - Przetwarzanie i generowanie
zlokalizowanych liczb.
Zend_Locale_Data - Pozyskiwanie zlokalizowanej formy
standardowych łańcuchów znaków - nazw państw, języków i
innych z CLDR.
TODO - Lokalizacja porządków sortowania
Czym jest locale
Każdy użytkownik komputera używa locale, nawet wtedy gdy o tym nie myśli. Aplikacje
nie posiadające obsługujące wielu zestawów językowych przeważnie wspierają
jedno określone locale (locale autora). Kiedy klasa lub funkcja używają lokalizacji,
mówi się że jest świadoma locale. W jaki sposób kod "wie" jakiego locale użytkownik
się spodziewa?
Łańcuch znaków lub obiekt identyfikujący locale daje klasie
Zend_Locale i jej klasom pochodnym dostęp do informacji
dotyczących języka oraz regionu, których użytkownik się spodziewa.
Na podstawie tych informacji dokonywane jest poprawne formatowanie, normalizacja
oraz konwersje.
Jak locale są reprezentowane
Identyfikatory locale składają się z informacji dotyczących języka użytkownika oraz
preferowanego/podstawowego regionu geograficznego (np. stan, województwo, land).
Łańcuchy identyfikatorów locale używane w Zend Framework przestrzegają międzynarodowych
standardów dotyczących skrótów języków i regionów. Zapisane są jako
język_REGION. Obie części są utworzone z liter znaków wchodzących w skład
ASCII.
W przeciwieństwie do popularnego osądu istnieją identyfikatory locale składające
się z więcej niż 2 liter. Dodatkowo istnieją języki i regiony których skróty
również są zawarte w więcej niż 2 literach. Mając to na uwadze, należy wystrzegać
się własnoręcznego wydobywania oznaczenia języka czy regionu z pełnego\
identyfikatora locale. Zamiast tego należy skorzystać z
Zend_Locale. W przeciwnym przypadku efekty działania kodu
mogą okazać się niespodziewane.
Użytkownik z USA może spodziewać się języka angielskiego (English)
oraz regionu USA. Daje to identyfikator locale: "en_US".
Użytkownik w Niemczech będzie się spodziewał języka niemieckiego
(Deutsch) oraz regionu Niemcy (Deutschland) co da
locale "de_DE". Aby zasięgnąć szczegółowych informacji o konkretnym
identyfikatorze locale do użytku w Zend Framework należy zapoznać się z listą
identyfikatorów locale.
Wybranie konkretnego locale
Użytkownik z Niemiec przebywający w Ameryce mógłby oczekiwać języka
Deutsch i regionu USA ale podobne niestandardowe
połączenia nie są wspierane w takim stopniu jak pełnoprawne locale. Jeśli podana
zostanie nieprawidłowa kombinacja to nastąpi automatyczne odrzucenie kodu regionu.
Dla przykładu, "de_IS" zostałoby ograniczone do "de" a "xh_RU" - do "xh" ponieważ
żadna z tych kombinacji nie jest poprawna. Dodatkowo, jeśli język podanej kombinacji
nie jest wspierany (np. "zz_US") lub nie istnieje to zostanie użyte domyślne locale
"root", które ma domyślny zestaw definicji międzynarodowych oznaczeń dat, czasów,
liczb, walut itp. Proces odrzucania kodu regionu zależy również od żądanych informacji.
Niektóre kombinacje języków i regionów mogą być odpowiednie dla określonego
rodzaju danych (np. dat) ale nie dla innych (np. format waluty).
Należy mieć na uwadze zmiany historyczne ponieważ komponenty Zend Framework mogą nie
być "świadome" częstych zmian stref czasowych na przestrzeni lat w wielu regionach.
Przykładowo, pod tym linkiem widać
historyczną listę szeregu zmian jakich dokonywały rządy w stosunku do
występowania tzw. czasu letniego a nawet obowiązującej strefy czasowej. Przez to,
podczas obliczeń na datach, komponenty Zend Framework nie będą brały tych zmian pod
uwagę. Zamiast tego zwrócony zostanie rezultat wynikający z użycia strefy czasowej
wg. obecnych zasad dotyczących czasu letniego i strefy czasowej konkretnego regionu.
Wybranie odpowiedniego locale
W większości przypadków new Zend_Locale() automatycznie wybierze poprawne
locale zachowując uprzywilejowanie w stosunku do danych udostępnionych przez
przeglądarkę użytkownika. W przypadku użycia
new Zend_Locale(Zend_Locale::ENVIRONMENT) pierwszym źródłem informacji
o locale stanie się konfiguracja serwera hostingowego tak jak opisano niżej.
Automatyczny wybór locale
Algorytm automatycznego szukania locale używany przez Zend_Locale
używa trzech źródeł danych:
const Zend_Locale::BROWSER - Przeglądarka użytkownika
przy każdym żądaniu, dostarcza informacji, które przekładane są na zmienną
globalną PHP o nazwie
$_SERVER['HTTP_ACCEPT_LANGUAGE'].
Jeśli brak jest odpowiedniego locale to następnym źródłem branym pod
uwagę jest ENVIRONMENT i w ostateczności
FRAMEWORK.
const Zend_Locale::ENVIRONMENT - PHP
udostępnia locale serwera hostingowego poprzez wewnętrzną funkcję
setlocale(). Jeśli brak jest odpowiedniego locale
to kolejnym źródłem branym pod uwagę staje się
FRAMEWORK i w ostateczności
BROWSER.
const Zend_Locale::FRAMEWORK - W momencie, w którym
Zend Framework będzie miał wystandaryzowany sposób określania
domyślnych wartości komponentów (cecha jeszcze niedostępna) wtedy użycie
tej stałej spowoduje wybranie locale na podstawie tych domyślnych wartości.
W przypadku braku odpowiedniego locale kolejnym źródłem branym pod uwagę
będzie ENVIRONMENT i na końcu
BROWSER.
Użycie automatycznego locale
Zend_Locale oferuje trzy dodatkowe locale. Nie należą one
do żadnego języka ani regionu. Są to locale "automatyczne" co oznacza, że działają
na podobnej zasadzie co metoda getDefault() ale bez potrzeby
tworzenia instancji. Locale "automatyczne" mogą być użyte w każdym momencie, w którym
możliwe byłoby zdefiniowanie locale w standardowy sposób. Dzięki temu można ich używać
w łatwy sposób np. w sytuacji pracy z locale udostępnianym przez przeglądarkę.
Istnieją trzy rodzaje takich locale, których zachowanie nie jest standardowe:
'browser' - klasa Zend_Locale powinna
użyć informacji dostarczanych przez przeglądarkę użytkownika. Te dane są
udostępniane przez PHP w globalnej zmiennej
$_SERVER['HTTP_ACCEPT_LANGUAGE'].
Jeśli przeglądarka użytkownika informuje o więcej niż jednym locale
Zend_Locale użyje pierwszego z listy. W przypadku
nie podania żadnego locale przez przeglądarkę (lub gdy skrypt jest
uruchomiony z linii poleceń) zostanie użyte "automatyczne" locale
'environment'.
'environment' - klasa Zend_Locale
powinna brać pod uwagę informacje przekazane przez serwer hostingowy.
Są one udostępnione przez PHP poprzez wewnętrzną funkcję
setlocale().
Jeśli środowisko serwera informuje o więcej niż jednym locale
Zend_Locale użyje pierwszego z listy. W przypadku
nie podania żadnego locale zostanie użyte "automatyczne" locale
'browser'.
'auto' - klasa Zend_Locale powinna
automatycznie wykryć locale, które nadaje się do użycia. W pierwszej
kolejności będzie próbować znaleźć locale użytkownika, w przypadku
niepowodzenia pod uwagę brane będzie środowisko serwera hostingowego.
Jeśli nie uda się wykryć locale, klasa rzuci wyjątek i powiadomi o
niepowodzeniu.
Użycie automatycznego locale
Użycie domyślnego locale
W niektórych środowiskach automatyczna detekcja locale nie jest możliwa. Można się tego
spodziewać w przypadku uruchomienia skryptu z linii poleceń lub gdy przeglądarka
użytkownika nie ma zdefiniowanego języka a domyślnym locale serwera hostingowego
jest 'C' lub inne niestandardowe locale.
W takim przypadku Zend_Locale rzuci wyjątek z informacją o
niepowodzeniu w automatycznym wykryciu locale. Istnieją dwa wyjścia z tej sytuacji.
Pierwszym jest ręczne ustawienie nowego locale, drugim - zdefiniowanie domyślnego
locale.
Obsługa wyjątków locale
To podejście ma jedną wadę. Wymusza ustawienie obiektu locale poprzez
Zend_Locale w każdej klasie. W przypadku używania wielu klas
może to stanowić duży problem.
Od Zend Framework 1.5 istnieje o wiele lepszy sposób na obsługę takiej sytuacji.
Można ustawić domyślne locale za pomocą statycznej metody
setDefault(). Oczywiście każde nieznane lub źle sformułowane
locale również spowoduje rzucenie wyjątku. setDefault()
powinna być wywołana przed uruchomieniem dowolnej klasy używającej
Zend_Locale. Tak jak w poniższym przykładzie:
Ustawienie domyślnego locale
W przypadku niepowodzenia automatycznego wykrycia locale użyte zostanie locale
de. W przeciwnym przypadku - klasa użyje wykrytego locale.
Klasy ZF "świadome" locale
Klasy, które są "świadome" locale w Zend Framework polegają na
Zend_Locale w kwestii automatycznego wybrania locale na
zasadach opisanych wcześniej. Np. w aplikacji webowej Zend Framework, utworzenie
obiektu klasy Zend_Date bez podania locale spowoduje, że
powstały obiekt będzie miał locale ustawione na podstawie przeglądarki użytkownika.
Format daty domyślnie dostosowuje się do przeglądarki
Aby zmienić domyślne zachowanie i zmusić komponenty Zend Framework "świadome" locale
do użytku określonego locale (obojętnie od ustawień przeglądarki użytkownika) należy
podać nazwę locale w trzecim argumencie konstruktora.
Zmiana domyślnego wykrywania locale
Jeśli jest wiadomo, że wiele obiektów powinno użyć określonego locale to należy się
upewnić, że będą miały to locale określone. Dzięki temu można uniknąć dodatkowego
czasu pracy skryptu wynikającego z wyszukiwania odpowiedniego locale.
Optymalizacja wydajności dzięki określeniu locale
Locale obowiązujące w całej aplikacji
Zend Framework umożliwia zdefiniowanie locale obowiązującego w całej aplikacji.
Polega to na wywołaniu obiektu klasy Zend_Locale i umieszczeniu
go w rejestrze z kluczem 'Zend_Locale'. Dzięki temu ta instancja będzie używana we
wszystkich klasach "świadomych" locale Zend Framework. Ten sposób umożliwia
ustawienie locale w rejestrze i uwolnienie się od potrzeby zwracania uwagi na to
zagadnienie. Ustawione locale zostanie automatycznie użyte we wszystkich przypadkach.
Przykładowe użycie znajduje się w poniższym przykładzie:
Użycie locale obowiązującego w całej aplikacji
getLocale();
echo $date->getDate();
]]>
Zend_Locale_Format::setOptions(array $options)
Opcja 'precision' służy do obcięcia lub wydłużenia liczby. Wartość '-1' wyłącza
możliwość modyfikacji ilości liczb po przecinku. Opcja 'locale' jest pomocna podczas
przetwarzania liczb i dat z użyciem separatorów oraz nazw miesięcy.
Opcja formatów dat 'format_type' umożliwia wybór pomiędzy standardem
CLDR/ISO oraz wyrażeniami funkcji PHP date().
Opcja 'fix_date' umożliwia podanie formatu liczb do użytku z metodą
toNumber() (więcej informacji znajduje się w rozdziale
).
Opcja 'date_format' może być użyta do określenia domyślnego formatu daty ale należy
zachować ostrożność przy używaniu getDate(),
checkdateFormat() and getTime().
Aby ich pomyślnie używać należy umieścić w ich opcjach następującą tablicę:
array('date_format' => null, 'locale' => $locale).
Daty przyjmują domyślny format przeglądarki
'en_US',
'fix_date' => true,
'format_type' => 'php'));
]]>
Aby uzyskać standardowe definicje locale można użyć stałej
Zend_Locale_Format::STANDARD. Ustawienie w opcji
date_format na Zend_Locale_Format::STANDARD spowoduje
użycie standardowej definicji formatu zawartej w bieżącym locale. Umieszczenie tej
stałej w opcji number_format spowoduje użycie standardowego
formatu liczb z bieżącego locale. Ustawienie stałej w opcji locale
spowoduje użycie standardowego locale dla środowiska bądź przeglądarki.
Użycie standardowych definicji w metodzie setOptions()
'en_US',
'date_format' => 'dd.MMMM.YYYY'));
// nadpisanie globalnego formatu daty
$date = Zend_Locale_Format::getDate('2007-04-20',
array('date_format' =>
Zend_Locale_Format::STANDARD);
// globalne ustawienie standardowego locale
Zend_Locale_Format::setOptions(array('locale' => Zend_Locale_Format::STANDARD,
'date_format' => 'dd.MMMM.YYYY'));
]]>
Przyspieszenie Zend_Locale i jej klas pochodnych
Można przyspieszyć działanie klasy Zend_Locale i jej klas
pochodnych poprzez użycie Zend_Cache. W przypadku używania
Zend_Locale osiągnąć to można za pomocą statycznej
metody Zend_Locale::setCache($cache).
Klasę Zend_Locale_Format można przyspieszyć używając opcji
cache w
Zend_Locale_Format::setOptions(array('cache' => $adapter));.
Jeśli używane są obie klasy należy ustawić cache jedynie dla
Zend_Locale. W przeciwnym przypadku ostatni ustawiony cache
nadpisze wcześniejszy. Dla ułatwienia istnieją również statyczne metody
getCache(), hasCache(),
clearCache() oraz removeCache().
Jeśli deweloper nie ustawi własnoręcznie cache'u Zend_Locale
automatycznie zrobi to za niego. W niektórych sytuacjach może zajść potrzeba rezygnacji
z użycia cache'u, nawet za cenę wydajności. Wtedy należy użyć statycznej metody
disableCache(true). Oprócz wyłączenia ewentualnego wcześniej
ustawionego cache'u (bez usuwania jego zawartości) metoda ta zapobiega generowaniu
cache'u (jeśli nie był wcześniej ustawiony).