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()
.
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
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.
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.
Uwaga
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.
Przykład 519. Wybranie konkretnego locale
$locale = new Zend_Locale('de_DE'); // Język niemiecki _ Niemcy
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.
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.
Przykład 520. Automatyczny wybór locale
$locale = new Zend_Locale(); // domyślne zachowanie, tak samo jak wyżej $locale1 = new Zend_Locale(Zend_Locale::BROWSER); // preferencja ustawień serwera $locale2 = new Zend_Locale(Zend_Locale::ENVIRONMENT); // perferencja ustawień frameworka $locale3 = new Zend_Locale(Zend_Locale::FRAMEWORK);
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ę jestENVIRONMENT
i w ostatecznościFRAMEWORK
. -
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ściBROWSER
. -
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ędzieENVIRONMENT
i na końcuBROWSER
.
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'
- klasaZend_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'
- klasaZend_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'
- klasaZend_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.
Przykład 521. Użycie automatycznego locale
// bez automatycznej detekcji //$locale = new Zend_Locale(Zend_Locale::BROWSER); //$date = new Zend_Date($locale); // z automatyczną detekcją $date = new Zend_Date('auto');
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.
Przykład 522. Obsługa wyjątków locale
// w pliku bootstrap try { $locale = new Zend_Locale('auto'); } catch (Zend_Locale_Exception $e) { $locale = new Zend_Locale('de'); } // w modelu/kontrolerze $date = new Zend_Date($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:
Przykład 523. Ustawienie domyślnego locale
// w pliku bootstrap Zend_Locale::setDefault('de'); // w modelu/kontrolerze $date = new Zend_Date();
W przypadku niepowodzenia automatycznego wykrycia locale użyte zostanie locale de. W przeciwnym przypadku - klasa użyje wykrytego 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.
Przykład 524. Format daty domyślnie dostosowuje się do przeglądarki
$date = new Zend_Date('2006',Zend_Date::YEAR);
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.
Przykład 525. Zmiana domyślnego wykrywania locale
$usLocale = new Zend_Locale('en_US'); $date = new Zend_Date('2006', Zend_Date::YEAR, $usLocale); $temp = new Zend_Measure_Temperature('100,10', Zend_Measure::TEMPERATURE, $usLocale);
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.
Przykład 526. Optymalizacja wydajności dzięki określeniu locale
$locale = new Zend_Locale(); $date = new Zend_Date('2006', Zend_Date::YEAR, $locale); $temp = new Zend_Measure_Temperature('100,10', Zend_Measure::TEMPERATURE, $locale);
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:
Przykład 527. Użycie locale obowiązującego w całej aplikacji
// w pliku bootstrap $locale = new Zend_Locale('de_AT'); Zend_Registry::set('Zend_Locale', $locale); // w modelu lub kontrolerze $date = new Zend_Date(); // print $date->getLocale(); echo $date->getDate();
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
„Number localization”).
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).
Przykład 528. Daty przyjmują domyślny format przeglądarki
Zend_Locale_Format::setOptions(array('locale' => '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.
Przykład 529. Użycie standardowych definicji w metodzie setOptions()
Zend_Locale_Format::setOptions(array('locale' => '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'));
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).