PHP/Internacjonalizacja: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Zyx (dyskusja | edycje)
utworzenie
 
Zyx (dyskusja | edycje)
dokończenie
Linia 13:
?></nowiki>
 
W zależności od komputera, pokaże on napis ''żółta woda'' albo ''ŻóŁta woda''. Zastanówmy się, skąd jeden interpreter wiedział, że ''Ż'' jest literą polskiego alfabetu, która również podlega zamianie, a drugi nie? Odpowiedź kryje się w ustawieniach regionalnych tych maszyn, znanych z terminologii linuksowej pod nazwą ''locale''. PHP domyślnie wykonuje wszystkie operacje na tekstach, ustawia formatowanie walut oraz liczb na podstawie ustawień systemowych. Aby zmienić opcje regionalne dla aktualnie wykonywanego skryptu, należy skorzystać z funkcji ''setlocale()''. Za pierwszy parametr podajemy flagi określające, które aspekty regionalizacji chcemy zmodyfikować, a wszystkie kolejne to identyfikatory możliwych ustawień. PHP będzie próbował każdego z nich po kolei, aż trafi na taki, który jest zainstalowany w systemie. Dzięki temu jedną funkcją można przystosować skrypt do pracy zarówno z serwerami opartymi o Linux, jak i Windows. Poniżej przedstawiamy jeszcze raz skrypt ''strtolower()'' skonfigurowany do pracy z polskimi ustawieniami:
 
<nowiki><?php
 
echo date('F');
setlocale(LC_ALL, 'pl_PL', 'pl', 'Polish_Poland.28592');
 
echo strtolower('ŻÓŁTA WODA');
 
?></nowiki>
 
Pierwsze dwa identyfikatory (''pl_PL'' oraz ''pl'') dotyczą systemu Linux. Trzeci przeznaczony jest dla rodziny Windows. Listę aktualnie zainstalowanych ustawień można znaleźć w ''Panel sterowania → Opcje regionalne → Zaawansowane''.
Nawet jeśli poprzedni skrypt zamienił Ci poprawnie litery, nie oznacza to, że ten także pokaże Ci polską nazwę miesiąca, np. ''Kwiecień''. Jak widać, poleganie na systemowych ustawieniach jest dosyć niepewne, zwłaszcza gdy zamierzamy rozpowszechniać naszą aplikację i będzie ona pracować na różnych serwerach. PHP posiada funkcję ''setlocale()'' służącą do zmiany ustawień regionalnych używanych aktualnie przez interpreter. Za pierwszy parametr podajemy flagi określające, które aspekty regionalizacji chcemy zmodyfikować, a wszystkie kolejne to identyfikatory możliwych ustawień. PHP będzie próbował każdego z nich po kolei, aż trafi na taki, który jest zainstalowany w systemie. Dzięki temu jedną funkcją można przystosować skrypt do pracy zarówno z serwerami opartymi o Linux, jak i Windows. Poniżej przedstawiamy jeszcze raz skrypt ''strtolower()'' skonfigurowany do pracy z polskimi ustawieniami:
 
=== Polska data ===
 
Pisząc aplikację przeznaczoną na rynek międzynarodowy, powinniśmy pomyśleć także o formatowaniu dat. Poznana już przez nas funkcja ''date()'' nawet przy pracy z polskimi ustawieniami wyświetli nam angielskie nazwy miesięcy. Sprawę rozwiązuje ''strftime()'', przy której musimy pamiętać o innej składni formatowania daty. Przyjrzyjmy się przykładowi:
 
<nowiki><?php
 
setlocale(LC_ALL, 'pl_PL', 'pl', 'Polish_Poland.28592');
// Dzień - nazwa miesiąca - rok
echo strftime('%d %B %Y');
 
?></nowiki>
echo strtolower('ŻÓŁTA WODA');
 
Po uruchomieniu okazuje się, że nawet ten skrypt nie jest doskonały, ponieważ wprawdzie generuje spolszczoną datę, ale niepoprawną gramatycznie! Otrzymujemy komunikat np. ''17 kwiecień 2006'', a tymczasem poprawną formą jest ''17 kwietnia 2006''. W niuanse gramatyczne wgłębiać się nie będziemy - po prostu jest tak, a nie inaczej i niestety musimy samodzielnie napisać sobie funkcję, która nam to przeformatuje. Umożliwiając dodawanie obsługi nowych języków do aplikacji, powinniśmy pomyśleć o możliwości jej podmiany tak, aby twórca nakładki językowej mógł zaprogramować datę zgodnie z wymogami swego języka. Oto przykład takiej funkcji dla języka polskiego. Jest to modyfikacja ''strftime()'' dodająca nowy znacznik - '''%F''' będący właśnie odmienioną nazwą miesiąca:
 
<nowiki><?php
function localStrftime($format, $timestamp = 0)
{
if($timestamp == 0)
{
// Sytuacja, gdy czas nie jest podany - używamy aktualnego.
$timestamp = time();
}
 
// Nowy kod - %F dla odmienionej nazwy miesiąca
if(strpos($format, '%F') !== false)
{
$mies = date('m');
// odmienianie
switch($mies)
{
case 1:
$mies = 'stycznia';
break;
case 2:
$mies = 'lutego';
break;
case 3:
$mies = 'marca';
break;
case 4:
$mies = 'kwietnia';
break;
case 5:
$mies = 'maja';
break;
case 6:
$mies = 'czerwca';
break;
case 7:
$mies = 'lipca';
break;
case 8:
$mies = 'sierpnia';
break;
case 9:
$mies = 'września';
break;
case 10:
$mies = 'października';
break;
case 11:
$mies = 'listopada';
break;
case 12:
$mies = 'grudnia';
break;
}
// dodawanie formatowania
return strftime(str_replace('%F', $mies, $format), $timestamp);
}
return strftime($format, $timestamp);
} // end localStrftime();
echo localStrftime('%d %F %Y');
 
?></nowiki>
 
Teraz otrzymujemy prawidłowy tekst: ''17 kwietnia 2006''.
Pierwsze dwa identyfikatory (''pl_PL'' oraz ''pl'') dotyczą systemu Linux. Trzeci przeznaczony jest dla rodziny Windows.
 
=== Wielojęzyczny interfejs ===
Kolejnym krokiem na drodze umożliwienia obcokrajowcom przeglądania naszych witryn jest stworzenie kilku wersji językowych. Realizuje się to, tworząc nakładki językowe, w których poszczególnym komunikatom interfejsu przypisane są odpowiednie identyfikatory. Następnie w kodzie aplikacji, zamiast pisać odpowiednie teksty, wywołujemy specjalną funkcję i podajemy ID tekstu do wstawienia. Ta wprowadza w tym miejscu odpowiedni tekst w zależności od wybranego języka. Ponieważ nie znamy jeszcze programowania obiektowego, będziemy musieli napisać odpowiednią bibliotekę strukturalnie:
 
<nowiki><?php
 
$preferencje = array();
$tekstyI18n = array();
function zainstalowanyJezyk($jezyk)
{
// Tutaj mozemy umiescic kod sprawdzajacy, czy nasz serwis posiada
// wersje w podanym jezyku
switch($jezyk)
{
case 'pl':
case 'de':
case 'fr':
case 'en':
return true;
}
return false;
} // end zainstalowanyJezyk();
 
function uzyjJezyk($jezyk)
{
global $preferencje;
if(zainstalowanyJezyk($jezyk))
{
$preferencje['jezyk'] = $jezyk;
}
} // end uzyjJezyk();
function wybierzGrupe($grupa)
{
global $preferencje, $tekstyI18n;
$tekstyI18n[$grupa] = @parse_ini_file('jezyki/'.$preferencje['jezyk'].'/'.$grupa.'.php');
} // end wybierzGrupe();
function wstaw($grupa, $id)
{
global $tekstyI18n;
if(isset($tekstyI18n[$grupa][$id]))
{
return $tekstyI18n[$grupa][$id];
}
return strtoupper($grupa.':'.$id);
} // end wstaw();
 
?></nowiki>
W powyższym kodzie tekstom przypisane są nie tylko identyfikatory - podzielono je także na ładowane oddzielnie grupy. W ten sposób strona z newsami może załadować sobie tylko treści komunikatów dla newsów, z pominięciem np. tekstów przeznaczonych dla forum dyskusyjnego. Oto opis funkcji:
* ''zainstalowanyJezyk()'' - zwraca '''true''', jeżeli podany język jest obsługiwany przez nasz skrypt.
* ''uzyjJezyk()'' - ustawia podany język.
* ''wybierzGrupe()'' - ładuje podaną grupę na podstawie aktualnych ustawień języka. Do wczytania grupy używamy funkcji ''parse_ini_file()'' przetwarzającej podany plik identycznym parserem, jak ten, który przetwarza ''php.ini''. Dzięki temu zyskujemy duże możliwości małym kosztem.
* ''wstaw()'' - wstawia tekst o podanym ID należący do odpowiedniej grupy na podstawie aktualnych ustawień językowych.
 
Pliki językowe należy umieszczać w katalogu ''jezyki/kodjezyka/nazwagrupy.php''. Oto przykładowy plik ''jezyki/pl/global.php'':
 
<nowiki>komunikat = "Komunikat"
hello_world = "Witaj swiecie!"</nowiki>
 
Korzysta z niego poniższy przykład:
 
<nowiki><?php
 
require('./i18n.php');
uzyjJezyk('pl');
wybierzGrupe('global');
echo '<h2>'.wstaw('global', 'komunikat').'</h2>';
echo '<p>'.wstaw('global', 'hello_world').'</p>';
 
?></nowiki>
 
Na początku ładujemy naszą bibliotekę i wybieramy język. Później wczytujemy grupę i wyświetlamy komunikaty. To wszystko - jesteśmy posiadaczami wielojęzycznego interfejsu. Pamiętaj, że jest to tylko przykładowy kod. Aby był on w pełni sprawny, powinieneś umożliwić użytkownikowi zmianę języka, a także zadbać o prawidłową obsługę błędów. Zwróć też uwagę, że dane obecnego systemu przechowywane są w tablicach globalnych. Nie jest to rozwiązanie najlepsze, ale na tym etapie wiedzy powinno nam wystarczyć. Kiedy poznamy programowanie obiektowe, będziesz mógł przepisać ten kod z jego wykorzystaniem, dzięki czemu zyska on na elegancji i bezpieczeństwie.
 
=== Automatyczna detekcja języka ===
Przeglądarka internetowa to cenne źródło informacji o internaucie. Przesyła ona m.in. informację o języku, w jakim ona pracuje. Możemy przyjąć, że jest to rodzimy język użytkownika i na podstawie tego automatycznie ustawić mu odpowiednią wersję językową strony. Całość zawarta jest w ''$_SERVER['HTTP_ACCEPT_LANGUAGE']''.
 
<nowiki><?php
 
$zainstalowane = array( // 1
'pl' => 'polski',
'en' => 'angielski',
'fr' => 'francuski'
);
 
$jezyki = explode(';', $_SERVER['HTTP_ACCEPT_LANGUAGE']); // 2
$jezyki = explode(',', $jezyki[0]);
$uzyty = null;
foreach($jezyki as $jezyk)
{
if(isset($zainstalowane[$jezyk])) // 3
{
$uzyty = $jezyk;
break;
}
}
if(is_null($uzyty)) // 4
{
$uzyty = 'en';
}
echo 'Wybrany język to '.$zainstalowane[$uzyty];
 
?></nowiki>
 
Analiza powyższego skryptu:
# Tablica z nazwami zainstalowanych języków - tylko do celów kontrolnych.
# Najpierw musimy usunąć niepotrzebne nam informacje. ''HTTP_ACCEPT_LANGUAGE'' posiada odpowiedni format informacji: ''języki;ważności'', gdzie kody języków oddzielone są przecinkami. Druga część nie jest nam w ogóle potrzebna i możemy ją usunąć. Stąd w drugim explode pojawia się odwołanie ''$jezyki[0]'' wskazujące po prostu na pierwszą, istotną część przekazu.
# Pętlą przelatujemy całą tablicę. Jeśli zauważymy, że dany język jest zainstalowany, ustawiamy go i przerywamy pętlę.
# Jeżeli zmienna ''$uzyty'' ma w tym miejscu dalej domyślną wartość '''null''', oznacza to, że żaden z języków użytkownika nie jest obsługiwany. W tym wypadku zmuszamy go do czytania po angielsku.
 
Naturalnie wypadałoby zadbać, aby wybierałka była bardziej przyjazna użytkownikowi i na pierwszym miejscu sprawdzała informacje zapisane w ciastkach. Dopiero kiedy takowych nie będzie, można pobawić się w detekcję. Inaczej użytkownik nie będzie w stanie zmienić "narzuconego" mu języka nawet, jeśli jego własny będzie obsługiwany przez naszą witrynę.
 
=== Zakończenie ===
Internacjonalizacja jest bardzo ważnym zagadnieniem. Jeśli naprawdę myślimy o ekspansji na rynki zagraniczne, nie lekceważmy jej. Czas zainwestowany w uczynienie serwisu bardziej przyjaznym obcokrajowcom w przyszłości zaprocentuje ich dobrymi wrażeniami z pobytu u nas.