FK~

Moje odkazy

Ostatní odkazy

Close Windows
Nenajdete mě na Facebooku ani Twitteru
Rozpad EU
Jsem členem FSF
There Is No Cloud …just other people's computers.
Sane software manifesto / Manifest příčetného softwaru

Java na serveru: lokalizace a formátování

vydáno: 18. 2. 2010 17:38, aktualizováno: 18. 7. 2020 13:42

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 svobodného softwaru.

Logo OpenJDK (průhledné 1)

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=\u010De\u0161tina. 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.

Lokalizační soubory v Netbeans

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 souboru preklady.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.

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?jazyk=csformatovani.jsp?jazyk=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ášní 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 a zdroje:

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

Témata: [Java]

Komentáře čtenářů

Za tento článek zatím nikdo autora nekamenoval

Přidat komentář

reagujete na jiný komentář (zrušit)
jméno nebo přezdívka
název příspěvku
webová stránka, blog
e-mailová adresa
nápověda: možnosti formátování
ochrana proti spamu a špatným trollům

Náhled komentáře