Wielojęzyczna strona w PHP
Tworząc strony internetowe, zwłaszcza te „firmowe”, czasami zachodzi potrzeba dodania obsługi innych języków, aniżeli tylko polski. Oczywiście możemy przygotować różne pliki HTML, ale problem zaczyna się, np. podczas zmiany struktury strony. Możemy temu zapobiec, tworząc prostą klasę w PHP, która w prosty sposób pozwoli nam na stworzenie wielojęzycznej strony.
Nasza klasa będzie opierała się o wzorzec projektowy – singleton. Pozwoli zaoszczędzić nam to ilość operacji wykonywanych przez serwer, a ponadto będziemy mogli odwoływać się do statycznych metod klasy.
Na początek struktura plików. Będzie się ona prezentowała następująco:
- index.php – główny plik naszej strony internetowej
- lang.class.php – plik z naszą klasą
- langs – folder przechowujący pliki językowe
- pl.php – poszczególne języki
- en.php
- de.php
- ru.php
Na samym początku musimy wiedzieć jak powinien wyglądać taki system. Ja dałem sobie takie wymagania:
- Maksymalnie uproszczona procedura dodawania nowego języka
- Szybka i bezproblemowa instalacja
- Powinien działać bez większych ingerencji w stronę
Nasz skrypt będzie działał w PHP5 – dzięki temu będziemy mogli bezproblemowo i szybko napisać nasz sterownik. Zaczniemy od pliku lang.class.php.
Główna klasa
<?phpclass Lang { private $languages = array('pl' => 'Polski', 'en' => 'English', 'de' => 'Deutsch', 'ru' => 'Русский'); private $current_language = FALSE; private $lines = array();private static $instance = FALSE;
Nasza klasa będzie się nazywała Lang. W jej nagłówku umieścimy kilka właściwości.
$languages będzie tablicą, która będzie przechowywała dostępne języki. Jako klucz podajemy skróconą nazwę języka, a jako zawartość nazwę języka. Dzięki temu, dopisanie nowego języka jest banalnie proste.
Właściwość $current_language będzie przechowywała aktualnie obsługiwany język, natomiast w $lines umieścimy poszczególne linie plików językowych, aby można było się do nich szybko odwołać.
Ostatnim elementem jest statyczna właściwość $instance. To dzięki niej będziemy mogli odwoływać się do klasy za pomocą statycznych metod. Ale o tym później.
Wszystkie te zmienne są prywatne, gdyż nie będzie potrzeby edytowania ich z zewnątrz.
public function __construct()
{
$this->set_language();
}
W konstruktorze wywołujemy metodę set_language(). Za jej pomocą rozpoznamy język, w jakim ma być wyświetlona strona. Konstruktor wywoła się tylko podczas zainicjowania klasy słowem kluczowym new. Jeśli będziemy chcieli wywołać jakąkolwiek metodę statycznie, konstruktor nie zostanie wywołany. Na szczęście za pomocą metody instance() będziemy mogli w łatwy sposób temu zapobiec.
public function instance() { if(self::$instance == FALSE) { self::$instance = new Lang; }return self::$instance; }
Metoda instance() jest jedną z najważniejszych w naszej klasie. Za jej pomocą możemy przenieść instancję do konkretnej metody, wywoływanej statycznie. Jej zadanie jest proste: sprawdzić, czy ktoś kiedykolwiek wywołał tą metodę. Jeśli nie, to tworzy nową klasę Lang i przypisuje ją do odpowiedniej zmiennej statycznej.
Zauważcie, że za pomocą słowa kluczowego new zostaje zainicjowana klasa, czyli konstruktor zostanie uruchomiony. W ten sposób będziemy mogli odwołać się wiele razy do tej metody, a klasa zostanie zainicjowana tylko i wyłącznie za pierwszym razem. Pozwoli to zaoszczędzić nam czas i pamięć serwera.
private function set_language() { if(isset($_GET['lang']) AND array_key_exists(($lang = strtolower($_GET['lang'])), $this->languages)) { $this->current_language = $lang; $_SESSION['site_lang'] = $lang; } elseif(isset($_SESSION['site_lang']) AND array_key_exists(($lang = strtolower($_SESSION['site_lang'])), $this->languages)) { $this->current_language = $lang; } else { $this->current_language = $this->default_language(); }$this->load_lang_file(); }
No i przyszedł czas na jedną z ważniejszych metod w naszej klasie. Za jej pomocą ustalamy niejako hierarchię, skąd skrypt ma czerpać wiedzę o języku. Najpierw skrypt sprawdzi, czy użytkownik nie wybrał języka za pomocą metody GET. Jeśli nie, sprawdzi jakiego języka używa użytkownik obecnie – za pomocą tablicy $_SESSION, a jeśli i to zawiedzie to użyje domyślnego języka, który zostanie zwrócony za pomocą metody default_language().
Zmiana języka będzie się odbywała za pomocą adresu. Czyli jeśli użytkownik kliknie na adres mojastrona.pl/?lang=de, będzie to informacja, że od tego momentu chce używać języka niemieckiego. W skrypcie sprawdzamy najpierw, czy taki język jest dostępny i może go używać, za pomocą funkcji array_key_exists(). Jeśli wszystko jest w porządku, zapisujemy do sesji informację, że od tego momentu strona ma być wyświetlana właśnie w tym języku.
Jeśli użytkownik wcześniej wybrał język, informacja o tym znajdzie się w zmiennej $_SESSION['site_lang']. Również sprawdzamy, czy język jest obsługiwany przez aplikację. Jeśli nie – stosujemy domyślną wersję językową.
private function default_language()
{
return current(array_keys($this->languages));
}
Metoda default_language() zwraca nam nazwę klucza pierwszego elementu tablicy. W naszym wypadku zwróci pl.
private function load_lang_file() { if(file_exists('langs/'.$this->current_language.'.php')) { include 'langs/'.$this->current_language.'.php';$this->lines = $lang; } }
Na co nam strona, bez odpowiedniego języka. Ta metoda na podstawie aktualnego języka pobiera odpowiedni plik językowy z katalogu langs/ i zapisuje wszystkie wartości do właściwości $this->lines.
public static function line($name = FALSE, $params = array()) { if(isset(self::instance()->lines[$name])) return vsprintf(self::instance()->lines[$name], $params);return FALSE; }
Czas na metodę, która będzie nam zwracała słowo/zdanie w odpowiednim języku. Metoda jest statyczna, więc będziemy mogli się do niej odwołać w sposób LANG::line(), bez inicjalizowania całej klasy. Całą „brudną” robotę odwala za nas metoda instance(). Za jej pomocą możemy odwołać się do właściwości i metod, które nie są dostępne statycznie.
Jak to działa? Użycie self::instance() powoduje tak naprawdę odwołanie się do $this. W metodzie instance() użyliśmy w rzeczywistości return $this. W ten sposób, już przy pierwszym użyciu metody, w tablicy lines pojawią się odpowiednie wpisy w konkretnym języku.
Dodatkowo do właściwości podajemy drugi parametr – $params. W ten sposób będziemy mogli wpływać na poszczególne zdania w konkretnym języku i umieszczać wewnątrz nich konkretne wyrazy. Całość sparsujemy funkcją vsprintf().
public static function get_language() { return self::instance()->current_language; }public static function get_languages() { return self::instance()->languages; }
Na koniec tworzymy dwie statyczne metody: get_language() oraz get_languages(). Pierwsza z nich pozwoli sprawdzić nam aktualny język na stronie, a druga zwróci nam wszystkie obsługiwane języki. W ten sposób możemy zrobić na przykład proste menu wyboru języka.
Na sam koniec nie zapominamy zamknąć klasy za pomocą }. Poza klasą tworzymy jeszcze jedną funkcję. Będzie wyglądała następująco:
function __($name = FALSE, $params = array())
{
return Lang::line($name, $params);
}
Dlaczego akurat __()? Otóż już tak się przyjęło, że funkcja odpowiadająca za tłumaczenie konkretnego zdania na różne języki przyjmuje taką nazwę. W ten sposób nie będziemy musieli za każdym razem pisać <? echo Lang::line('abc'); ?>, tylko <? echo __('abc') ?>.
Pliki językowe
W folderze langs/ umieszczamy poszczególne pliki językowe. Ich struktura jest identyczna:
pl.php
$lang['main_page'] = 'Strona główna';
$lang['welcome'] = 'Witam na mojej stronie! Nazywam się %s i mam %s lat.';
en.php
$lang['main_page'] = 'Homepage';
$lang['welcome'] = 'Welcome to my website! My name is %s and I have %s years.';
de.php
$lang['main_page'] = 'Homepage';
$lang['welcome'] = 'Willkommen auf meiner Website! Mein Name ist %s und ich haben %s Jahre.';
ru.php
$lang['main_page'] = 'Главная страница';
$lang['welcome'] = 'Добро пожаловать на мой сайт! Меня зовут %s и я %s лет.';
Każde zdanie posiada własny „klucz”, który pozwoli dopasować odpowiednie zdanie na odpowiednie miejsce. Ważne, aby wszystkie posiadały nazwę $lang.
Zauważmy, że w zdaniu Witam na mojej stronie! Nazywam się %s i mam %s lat., oraz w analogicznym zdaniu w innych językach, brakuje imienia i wieku. Jest on zastąpiony ciągiem znaków %s. Dzięki temu będziemy mogli wstawić te dane bezpośrednio w źródle strony.
Instalacja
Instalacja skryptu jest banalnie prosta. Wystarczy włączyć sesje, oraz wczytać plik z klasą.
<?
session_start();
require('lang.class.php');
?>
Nasz skrypt jest już gotowy do działania. Możemy teraz zacząć tłumaczyć naszą stronę.
<h1><?= __('main_page') ?></h1>
<p><?= __('welcome', array('Paweł', '11')) ?></p>
W ten sposób informujemy skrypt jakie słowa/zdania ma wyświetlać. Zauważmy, że do funkcji możemy przesyłać podane przez nas parametry. W zależności od języka na stronie pojawi się
<h1>Strona główna</h1>
<p>Witam na mojej stronie! Nazywam się Paweł i mam 11 lat.</p>
lub też
<h1>Homepage</h1>
<p>Willkommen auf meiner Website! Mein Name ist Paweł und ich haben 11 Jahre.</p>
Zauważmy, że w miejsce ciągu &s pojawiły się konkretne dane.
Zadbajmy też, aby użytkownik mógł zmienić język. Możemy do tego użyć metody get_languages(). Menu możemy zrobić na przykład tak:
<p>Wybierz język:</p>
<ul>
<? foreach(Lang::get_languages() as $lang=>$name): ?>
<li><a href="?lang=<?= $lang ?>"><?= $name ?></a></li>
<? endforeach; ?>
</ul>
czego efektem będzie:
<p>Wybierz język:</p>
<ul>
<li><a href="?lang=pl">Polski</a></li>
<li><a href="?lang=en">English</a></li>
<li><a href="?lang=de">Deutsch</a></li>
<li><a href="?lang=ru">Русский</a></li>
</ul>
Podsumowanie
Pisząc ten tutorial chciałem pokazać jak można w szybki i prosty sposób poznać takie rzeczy jak wzorce projektowe, instancje, itp. Ich używanie pozwala nie tylko zaoszczędzić czas podczas kodowania, ale również odciążyć serwer od niepotrzebnej pracy. Przy okazji napisaliśmy skrypt, który bardzo może nam pomóc w przekładzie strony na inne języki.
Na koniec umieszczam kod źródłowy głównej klasy, oraz pozostałych plików.

Dodaj komentarz do wpisu