Web byl již při svém vzniku zamýšlen jako prostředek pro sdílení informací po celém světě. Z hlediska technologií je sdílení informací poměrně dobře vyřešeno, slabým článkem je však člověk. Zatím neexistuje žádný jazyk, který by ovládali všichni uživatelé internetu. V mnoha případech je proto potřeba webové stránky nebo celé aplikace lokalizovat do několika jazyků.

Prvním stupněm lokalizace je takzvaná internacionalizace aplikace (značeno též i18n). Tím je myšlena schopnost aplikace zpracovávat data v různých jazycích a podle jejich pravidel (formátování čísel a data). Tuto schopnost dnes má většina aplikací, protože i základní prostředí pro tvorbu aplikací alespoň částečně podporují znakovou sadu Unicode a obsahují funkce pro snadné ovlivnění formátu zobrazovaných dat.

Samotná lokalizace (značeno též l10n) pak spočívá v kompletním přeložení uživatelského rozhraní aplikace do dalších jazyků.

Nabízí se několik přístupů. Asi nejjednodušší, ale v praxi nepoužitelný přístup, je zkopírování celé aplikace do dalšího adresáře a přeložení všech textů vyskytujících se ve zdrojovém kódu aplikace. Řešení má dvě velké nevýhody. Jednak se aplikace těžko udržuje, protože kód aplikační logiky se vyskytuje opakovaně v několika jazykových mutacích. Druhým problémem je zbytečně náročný překlad, protože opakující se fráze v textech se musí překládat několikrát. I přes evidentní nevýhody tohoto řešení jsem byl sám překvapen tím, kolikrát jsem se s touto metodou setkal.

Další řešení, jež si mnoho programátorů v nějaké podobě samostatně vytvořilo na koleně, spočívá v používaní konstant na místě textů. Tyto konstanty se pak za běhu programu nahradí odpovídajícím překladem z textového souboru nebo z databáze. Výsledné řešení však pořád není ideální. Text programu plný konstant není přehledný, musíme se aktivně starat o to, abychom všechny konstanty přeložili. Hledání překladu v textovém souboru nebo v databázi navíc není moc rychlé. Zapomenou-li se nějaké konstanty přeložit, často se místo nich ani nic nezobrazí.

Profesionálním řešením problému je použití knihovny GNU Gettext, která je dostupná i pro PHP. Knihovna nabízí funkci gettext(), kterou obalíme všechny řetězce, jež se mají lokalizovat.

Takže místo…

echo „Welcome!“;

…budeme psát:

echo gettext(„Welcome!“);

Existuje i alias funkce _, který umožňuje výrazně zkrátit zápis:

echo _(„Welcome!“);

Vidíme, že oproti původnímu zápisu stačilo přidat tři znaky navíc.

Funkce gettext() podle aktuálně nastaveného jazyka vrátí překlad odpovídající fráze. Pokud překlad neexistuje nebo jej nelze nalézt, vrací funkce původní argument – tedy text v angličtině nebo v jiném jazyce, ve kterém je aplikace primárně vyvíjena.

Výše zmíněný postup se musí použít i pro generování textu v HTML kódu, který se má překládat. Text se proto do HTML nevkládá přímo, ale musí se generovat pomocí PHP, jinak by jej gettext nemohl přeložit. Tedy místo…

<p>Enter your password: <input type=“password“ name=“pwd“></p>

…musíme použít poněkud delší:

<p>
    <?php echo _(„Enter your password:“)?>
    <input type=“password“ name=“pwd“>
</p>

Zvýšená námaha spojená s úpravou kódu aplikace se nám však brzy vrátí. Síla knihovny gettext totiž spočívá především v podpůrných nástrojích. Ty umožňují prohledat zdrojové kódy aplikace a vybrat v nich všechny texty k přeložení. Při tomto prohledání jsou samozřejmě odstraněny duplicity. Při změně aplikace lze snadno aktualizovat soubory s texty k překladu. Přeložené texty se pak pomocí dalších nástrojů převedou do binárního tvaru, který zajišťuje rychlý chod aplikace. Knihovna si navíc překlady ukládá do vyrovnávací paměti, takže je opravdu velmi rychlá. Zpomalení lokalizované aplikace oproti nelokalizované verzi je prakticky neměřitelné.

Instalace

Pro použití knihovny gettext musíme v PHP aktivovat příslušný modul. Většinou stačí v php.ini odkomentovat následující řádek:

extension=php_gettext.dll

V případě, že PHP kompilujeme, musíme pro zahrnutí podpory knihovny gettext přidat parametr --with-gettext.

Pro přípravu překladů pak potřebujeme pomocné nástroje, které jsou obsaženy v distribuci. Na unixových systémech gettext nejlépe nainstalujeme z balíčku určeného pro danou distribuci, v prostředí Windows je nejjednodušší si nainstalovat Cygwin, který umí emulovat mnoho unixových nástrojů. Při instalaci nesmíme zapomenout nainstalovat i balíček gettext. Pro bezproblémové použití s PHP je zapotřebí minimálně verze 0.12.

První použití

Pro použití stačí napsat obyčejný skript, ve kterém jsou všechny výskyty textových řetězců obaleny voláním funkce _() tak, jak ukazuje následující příklad.

Příklad 1.: Použití knihovny gettext – gettext.php

<?php
// parametr v URL slouží pro změnu jazyka
// pokud jazyk není zadán, předpokládá se angličtina
$lang = isset($_GET[„lang“]) ? $_GET[„lang“] : „en“;
// nastavení jazyka pro potřeby gettext
putenv(„LANG=$lang“);
setlocale(LC_ALL, $lang);
bindtextdomain(„messages“, realpath(„../locale“));
textdomain(„messages“);
// typické použití funkce gettext() pomocí aliasu
echo „<h1>“ . _(„Welcome!“) . „</h1>\n“;
// ukázka použití ngettext() pro různé tvary množného čísla
$num = rand(1,500);
for ($n = 1; $n <= $num; $n++)
{
    printf(ngettext(„You have %s new e-mail.“,
            „You have %s new e-mails.“, $n),
        $n);
    echo „<br>\n“;
}
// další ukázky použití
echo _(„Thank you for using our application“);
echo „<hr><p align=’left‘><em>“;
echo gettext(„Your webmaster“);
echo „</em></p>“;
?>

Skript můžeme rovnou použít, protože všechny funkce gettextu vrací zadané texty v případě, že nelze najít překlady.

Chceme-li nyní stránku přeložit do dalších jazyků, musíme z ní nejprve vytáhnout všechny texty k přeložení. Toho dosáhneme jednoduchým příkazem:

xgettext –from-code=cp1250 gettext.php

Vznikne soubor messages.po, který obsahuje všechny texty k přeložení (viz následující příklad). Parametr --from-code určuje kódování češtiny použité ve skriptu. Kromě hodnoty cp1250 můžeme například použít ISO-8859-2 nebo UTF-8.

Příklad 2.: Soubor messages.po s vyextrahovanými texty k překladu

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE’S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid „“
msgstr „“
„Project-Id-Version: PACKAGE VERSION\n“
„Report-Msgid-Bugs-To: \n“
„POT-Creation-Date: 2003-12-27 21:20+0100\n“
„PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n“
„Last-Translator: FULL NAME <EMAIL@ADDRESS>\n“
„Language-Team: LANGUAGE <LL@li.org>\n“
„MIME-Version: 1.0\n“
„Content-Type: text/plain; charset=CHARSET\n“
„Content-Transfer-Encoding: 8bit\n“
„Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n“
#: gettext.php:13
msgid „Welcome!“
msgstr „“
#: gettext.php:19
#, php-format
msgid „You have %s new e-mail.“
msgid_plural „You have %s new e-mails.“
msgstr[0] „“
msgstr[1] „“
#: gettext.php:24
msgid „Thank you for using our application“
msgstr „“
#: gettext.php:26
msgid „Your webmaster“
msgstr „“

Části označené jako msgid odpovídají jednotlivým textům v originálním skriptu. Do částí msgstr se pak píše překlad do jednotlivých jazyků. Knihovna gettext přitom předpokládá, že pro každý jazyk je překlad uložen v souboru, který je v adresáři cesta/jazyk/LC_MESSAGES. Vygenerovaný soubor messages.po proto přesuneme do adresáře ../locale/en/LC_MESSAGES.

Přípona souboru .po je zkratkou PO (Portable Object – přenositelný objekt). Pro použití gettextem je potřeba soubor převést z formátu PO do MO (Machine Object – strojový objekt), který je binární a umožňuje rychlé nalezení a vrácení překladu. Převod do binární podoby provedeme příkazem:

msgfmt messages.po

Vznikne tak binární soubor messages.mo.

Pro každý jazyk, do kterého chceme aplikaci přeložit, vytvoříme rovněž odpovídající adresář (například pro češtinu ../locale/cs/LC_MESSAGES) a nakopírujeme do něj soubor messages.po. Do souboru nyní doplníme české překlady (viz následující příklad). Mimo to je potřeba v souboru s překladem uložit informaci o použitém kódování (parametr Content-type). S ohledem na lokalizaci je dnes nejlepší všechny překlady udržovat v kódování UTF-8 a toto kódování používat rovněž pro stránky zasílané klientovi.

Příklad 3.: Soubor messages.po s doplněným českým překladem

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE’S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid „“
msgstr „“
„Project-Id-Version: PACKAGE VERSION\n“
„Report-Msgid-Bugs-To: \n“
„POT-Creation-Date: 2003-12-27 21:20+0100\n“
„PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n“
„Last-Translator: FULL NAME <EMAIL@ADDRESS>\n“
„Language-Team: LANGUAGE <LL@li.org>\n“
„MIME-Version: 1.0\n“
„Content-Type: text/plain; charset=UTF-8\n“
„Content-Transfer-Encoding: 8bit\n“
„Plural-Forms: nplurals=3;
    plural=n==1 ? 0 : n>=2 && n<=4 ? 1 : 2;\n“
#: gettext.php:13
msgid „Welcome!“
msgstr „Vítejte!“
#: gettext.php:19
#, php-format
msgid „You have %s new e-mail.“
msgid_plural „You have %s new e-mails.“
msgstr[0] „Máte %s novou e-mailovou zprávu.“
msgstr[1] „Máte %s nové e-mailové zprávy.“
msgstr[2] „Máte %s nových e-mailových zpráv.“
#: gettext.php:24
msgid „Thank you for using our application“
msgstr „Děkujeme vám za použití naší aplikace“
#: gettext.php:26
msgid „Your webmaster“
msgstr „Váš webmaster“

I pro český překlad vygenerujeme odpovídající binární podobu:

msgfmt messages.po

Pokud nyní ve skriptu správně inicializujeme gettext, budou se texty zobrazovat ve správném překladu. Předpokládáme-li, že proměnná $lang obsahuje kód požadovaného jazyka, pak jeho výběr provedeme pomocí příkazů:

putenv(„LANG=$lang“);
setlocale(LC_ALL, $lang);

Adresář, v němž se očekává struktura adresářů s překlady pro jednotlivé jazyky, nastavíme pomocí příkazů:

bindtextdomain(„messages“, realpath(„../locale“));
textdomain(„messages“);

Text messages přitom určuje jméno pro soubory s překlady. Můžeme použít libovolný jiný text. V našem případě používáme implicitní jména messages.po a messages.mo. Překlady jsou uloženy v adresáři locale, který je paralelní s adresářem se skripty. Pomocí funkce realpath() převedeme relativní cestu na absolutní, aby gettext hledal překlady na správném místě.

Nyní stačí restartovat webový server a překlady začnou fungovat, stačí ukázkovému skriptu předat parametr lang v URL, například http://localhost/gettext.php?lang=cs.

Pozn. aut.: Všechny ukázkové skripty a další soubory z této série článků si můžete stáhnout a vyzkoušet.

Odkazy a zdroje

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

1 Příspěvěk v diskuzi

  1. Zdar, super článek, jedna drobnost: Jako hodnotu proměnné $lang jsem musel zadat „cs_CZ.UTF-8“ a nikoli jen „cs“, jsem na Ubuntu. Jinak bezva a aji po pěti letech díky moc.

Odpovědět