Přejít k navigační liště

Zdroják » Různé » Java na webovém serveru: lokalizace a formátování

Java na webovém serveru: lokalizace a formátování

Články Různé

Po dvou databázových dílech seriálu, které mohly být pro některé čtenáře trochu náročnější, dnes přistoupíme k o něco lehčímu tématu. Budeme se zabývat lokalizací naší webové aplikace a formátováním výstupu. A na závěr si ukážeme jeden tip pro příznivce otevřeného softwaru.

Lokalizace softwaru neznamená jen překlad textů do příslušného jazyka, ale i další přizpůsobení aplikace místním podmínkám. S tím úzce souvisí formátování výstupu – jinak budete formátovat datum nebo měnu pro Angličany a jinak pro Čechy. A konec konců, lokalizační techniky se nám mohou hodit a při vývoji jednojazyčné aplikace  – texty externalizované do zvláštních souborů se snáze upravují a udržují  – např. když aplikaci používají různí zákazníci a každý má trochu jinou terminologii.

Jen pro připomenutí: jako obvykle si z Mercurialu stáhneme aktuální verzi zdrojových kódů k dnešnímu dílu seriálu:

$ hg pull
$ hg up "5. díl"

Případně si je můžete stáhnout jako bzip2 archiv přes web.

Lokalizace

S lokalizací aplikace je dobré začít co nejdříve, abychom později nemuseli procházet všechny stránky a hledat v nich nepřeložené texty (s tím by nám mohly pomoci nástroje v IDE, ale přiznám se, že raději provádím překlad ručně). Ještě je třeba poznamenat, že překlady se týkají jednak aplikace (texty tlačítek, položky v nabídkách, hlášky atd.) a jednak dat – obsahu (typicky v databázi). V dnešním díle se budeme zabývat pouze lokalizací aplikace.

Properties soubory

Pro ukládání lokalizovaných textů se v Javě používají tzv. .properties soubory. Jedná se o celkem obyčejné textové soubory se strukturou klíč=hodnota, ale v poněkud neobvyklém „kódování“  – místo např. jazyk=čeština v nich najdeme jazyk=u010Deu0161tina. Dříve se ke konverzi používal nástroj native2ascii a bylo potřeba udržovat dvě verze souboru (nativní kódování a escapované). V současnosti je tento postup zbytečný – vyspělé editory a IDE umí pracovat přímo s escapovaným souborem a konverze není potřeba. Pokud IDE nemáte, můžete použít prográmek Properties Editor.

Java - lokalizace

Chceme-li do .properties souboru vložit text obsahující konec řádku, použijeme n. Pokud naopak chceme zapsat nějaký dlouhý text, který má být ve výsledku na jednom řádku, můžeme řádek .properties souboru ukončit zpětným lomítkem a pokračovat na řádku dalším. XML to sice není, ale jelikož sem nijak složité struktury nezadáváme, dá se s těmito konvencemi vystačit.

Na balík lokalizovaných textů se odkazujeme podobně jako na třídu  – např. cz.frantovo.nekurak.preklady a také tento soubor vedle tříd ukládáme. Výchozí jazyková mutace se nachází v souborupreklady.properties a další jazyky v preklady_cs.properties, preklady_en.properties atd. Můžeme používat označení jazyka (cs, en) nebo upřesnit i stát (cs_CZ, en_US).

Vložení lokalizovaného textu

V JSP stránce, kterou chceme lokalizovat, musíme nejdřív importovat příslušný jmenný prostor:

xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"

nastavit balík s překlady:

<fmt:setBundle basename="cz.frantovo.nekurak.preklady" scope="application"/>

a potom už jen vkládáme lokalizované texty pomocí této značky:

<fmt:message key="klic"/>

Který jazyk se použije?

Volba jazyka se odvíjí od hlavičky Accept-language, kterou posílá prohlížeč v HTTP požadavku. Může v ní být uvedeno více jazyků (podle preferencí uživatele) a na serveru se vybere ten nejvhodnější (případně výchozí).

Jelikož někteří uživatelé mají prohlížeč špatně nastavený nebo pracují na cizím počítači, hodí se mít možnost tyto HTTP hlavičky přebít a nastavit jazyk programově (typicky uživatel klikne na odkaz a tím si zvolí požadovaný jazyk). Do JSP stránky si proto přidáme:

<c:if test="${param.jazyk != null}">
    <fmt:setLocale value="${param.jazyk}"/>
</c:if>

A uživatel si pak může nastavit jazyk pomocí GET parametru. Parametr není potřeba ošetřovat, protože pokud uživatel zadá neexistující jazyk, použije se ten výchozí a nic se neděje. Chyba nastane jen v případě, že by se pokoušel zadat něco jako cs_ nebo _xxx. Tím dojde k výjimce IllegalArgumentException: Empty country component nebo Missing language component, která vyústí v zobrazení chybové stránky (500 Interní chyba serveru). Nicméně nehrozí, že by tímto způsobem útočník získal přístup k nějakým souborům.

Všimněte si také, že server nám k lokalizované stránce automaticky doplnil hlavičku HTTP odpovědi Content-Language s příslušným jazykem.

Zaujaly vás možnosti Javy a chcete se dozvědět o tomto jazyce víc? Akademie Root nabízí školení Základy programovacího jazyka Java a Pokročilejší kurz jazyka Java, na nichž se naučíte, jak tento multiplatformní objektově orientovaný jazyk používat.

Formátování

Úlohou prezentační vrstvy je převést data (objekty) na tvar vhodný k zobrazení uživateli. Zatímco na nižších vrstvách pracujeme s objekty (např. instance java.util.Date) v (X)HTML potřebujeme jejich textový tvar. S prostým výstupem metody toString() si většinou nevystačíme, tak použijeme sofistikovanější přístup. Nejčastěji budeme řešit formátování data/času a čísel.

Číslo naformátujeme pomocí této značky:

<fmt:formatNumber value="1234567.123456" pattern="###,###.###"/>

Výsledkem je číslo zaokrouhlené na tři desetinná místa a s oddělovači tisíců. Automaticky se dodržují pravidla daného jazyka, a tak pro češtinu bude oddělovačem tisíců mezera a desetinných míst čárka, zatímco pro angličtinu to bude čárka a tečka.

Pro naformátování data a času použijeme takovéto značky:

<fmt:formatDate value="${datum}" pattern="dd.MM. yyyy HH:mm:ss"/>
<fmt:formatDate value="${datum}" pattern="dd.MM. yyyy"/>

Více viz soubor formatovani.jsp. Vyzkoušejte různé jazykové verze: formatovani.jsp?ja­zyk=cs a formatovani.jsp?ja­zyk=en

Chybějící hlavička Accept-language

Lidé se někdy ptají, JSTL fmt tag does not work in IE? nebo formatDate doesn’t work for Googlebot. Toto „záhadné“ chování aplikace je způsobeno poněkud zvláštní chybou. Pokud klient nepošle v HTTP požadavku hlavičku Accept-language (byť třeba s neexistujícím jazykem), nefunguje formátování data a čísel. A tak se můžete setkat s tím, že se

<fmt:formatNumber value="1234567890" pattern="###,###.###"/>

naformátuje jako „1234567890“ místo požadovaného „1 234 567 890“ (případně „1,234,567,890“ pro angličtinu). Přestože lokalizace textů funguje správně (použije se výchozí jazyk).

Chybu můžete ošetřit tímto kouskem kódu:

<c:if test="${header['Accept-language'] == null}">
    <fmt:setLocale value="cs"/>
</c:if>

Nebo – lépe – tímto nastavením ve web.xml:

<context-param>
    <param-name>javax.servlet.jsp.jstl.fmt.fallbackLocale</param-name>
    <param-value>cs</param-value>
</context-param>

Přibalení zdrojových kódů

Aplikace, kterou v tomto seriálu vyvíjíme, je svobodný software licencovaný pod Affero GPL. Uživatelům dáme možnost, aby si mohli stáhnout zdrojové kódy přímo z aplikace (ať už bude nasazena kdekoli). Vytvářet ZIP soubor se zdrojáky ručně by byla otrava, a tak si tento proces zautomatizujeme pomocí antovského skriptu.

Do souboru build.xml si přidáme úlohu:

<target name="-pre-dist">
    <property name="zdrojaky-archiv-soubor" value="web/nekurak.net-src.zip"/>
    <property name="zdrojaky-archiv-komentar" value="… Licence: GNU Affero GPL, verze 3"/>
    <property name="hashovani" value="SHA-512"/>

    <zip basedir="../../.." excludes="nekurak.net/html/**
      nekurak.net/.hg/**
      …"
      includes="nekurak.net/**"
      comment="${zdrojaky-archiv-komentar}"
      encoding="UTF-8"
      destfile="${zdrojaky-archiv-soubor}"/>
    <checksum file="${zdrojaky-archiv-soubor}" algorithm="${hashovani}" pattern="{0}  {1}"/>

    <manifest file="src/conf/MANIFEST.MF" mode="replace">
        <attribute name="Manifest-Version" value="1.0"/>
        <section name="Frantovo.cz">
        <attribute name="Kompiloval" value="${user.name}"/>
        <attribute name="java-version" value="${java.version}"/>
        <attribute name="java-vm-version" value="${java.vm.version}"/>
        <attribute name="os-name" value="${os.name}"/>
        <attribute name="os-arch" value="${os.arch}"/>
        <attribute name="os-version" value="${os.version}"/>
        </section>
    </manifest>
</target>

Úloha vytvoří ZIP archiv se zdrojovými kódy, vypočítá jeho SHA-512 hash a uloží do souboru (aby si uživatel mohl zkontrolovat, že během stahování nedošlo k chybě) a oba tyto soubory přibalí do výsledného .war archivu. Uživatel si pak může stáhnout zdrojové kódy přesně té verze, která byla použita pro kompilaci aplikace, se kterou právě pracuje. Nezkrácenou verzi najdete v souboru build.xml. Při psaní této úlohy musíme pečlivě nastavit atribut excludes, abychom do archivu nebalili věci, které tam nepatří (zkompilované třídy, .jar, .ear… soukromé soubory). Jméno a heslo k databázi naštěstí nastavujeme na úrovni aplikačního serveru, takže jejich únik nehrozí. Platí vlastně stejná pravidla jako pro posílání změn do verzovacího systému.

Závěr

Dnes jsme se naučili základy lokalizace a formátování výstupu a upozornili na jednu zajímavou chybu (snad vám to ušetřilo trochu času a nervů, které byste nad ní strávili). Příště se budeme věnovat autorizaci a autentizaci a také trochu pokročíme s naší aplikací, aby konečně dělala něco užitečného.

Odkazy

  • ISO-639 – Kódy jazyků – seznam na Wikipedii.
  • ISO-3166 – Kódy států – seznam na Wikipedii.
  • Properties Editor – editor lokalizačních souborů.
  • Affero GPL – článek „Affero GPLv3: Vydejte zdrojové kódy síťových aplikací!“
  • SimpleDateFormat – JavaDoc obsahující vzory pro psaní formátovacího řetězce.

Komentáře

Subscribe
Upozornit na
guest
10 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
pepa

Diky za vsechny dily serialu, tj. za konzistentni a primocary uvod do problematiky.

expee

S properties souborem to je vůbec docela zábavné. On totiž to „poněkud neobvyklé kódování“ vytváří NetBeans (viz. FaqI18nProjec­tEncoding). V Eclipse tyto soubory mám v běžném UTF-8 a funguje to bez problémů.

Takže vzniká situace, kdy vyvíjím v Eclipse, properties soubory mám v UTF-8 a pak potřebuji vytvořit nějaký swingový formulář, spustím NetBeans, ten mi properties překóduje do svého „kódování“ ISO-8859–1 + entity. V IDE je vše v pořádku, uložím, zobrazím v Eclipse a tím končím. Od té doby musím properties soubor editovat jen v NetBeans.

Nedoporučí někdo nějaký editor properties souboru do Eclipse, který si poradí s tímto NetBeans „kódováním“ ?

expee

Chtěl bych tento svůj komentář smazat…

Ono je to standardní chování, iso + entity…

Martiner
Shark_cz

Zajímalo by mě, jak se v Javě řeší překlady do jazyků které mají různý počet množných čísel. Pokud možno s ukázkou kódu. Jak by se vyřešil příklad:
Máte 1 nový email.
Máte 2 nové emaily.
Máte 5 nových emailů.
You have 1 new email.
You have 2(5) new emails.

Mastodont

New e-mail messages: X
Počet nových zpráv: X

v6ak

Nemůžu se zbavit pocitu, že problém pouze obcházíš a že původní znění mi zní lépe. Asi by to chtělo řešit nějakou komplexnější knihovnou.

Shark_cz

Ok, lokalizační soubor pro různé varianty vět v češtině vidím, jen mi není jasné za jakých okolností se volí jaká varianta. Vidím tam, že varianta začíná # nebo <.

Jak se docílí toho aby čísla končící na 1 s výjimkou 11 bylo zprávu. (Máte třicet jednu zprávu…)
Čísla končící 2, 3 a 4 (s výjimkou končících na 12, 13 a 14) byly zprávy. (Máte 102 zprávy…)
Ostatní čísla bylo zpráv. (Máte 112 zpráv…)

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.