Java Servlets – i18n

3. listopadu 2003

Táto tajomná skratka je odvodená od slova internacionalizácia, teda niečo ako „zmedzinárodnenie“. Inými slovami je to snaha, aby vytvorenú aplikáciu bolo možné dynamicky národnostne a kultúrne prispôsobovať. A to nie len čo sa týka jazyka, ale aj ďalších aspektov ako je mena, formát času a dátumu, oddeľovač desatinných miest a podobne.

V tomto článku vám chcem ukázať použitie Java Internationalization API, samozrejme na servletoch. Toto API umožňuje vytvárať textové, dátumové a iné výstupy na základe jazyka, resp. národnosti užívateľa. Výhodou použitia tohto programového rozhrania je, že zobrazovaný obsah nie je natvrdo zakódovaný v programe. To okrem iného znamená aj to, že nie je nutné opakovane vytvárať tie isté servlety pre podporu konkrétneho národného prostredia, čo znižuje prácnosť tvorby aplikácie a vytvára priestor pre rýchlejšie možnosti úprav. No a v neposlednom rade pridanie podpory pre ďalší jazyk je pri použití tohto API veľmi jednoduché.

Skôr ako si ukážeme nejaké príklady, povedzme si niečo o triedach Java I18n API. Zameriame sa na štyri najdôležitejšie:

  • java.util.Locale
    Objekt triedy Locale reprezentuje geografické, politické a kultúrne špecifiká krajín a národov. Ale je to iba identifikátor. Neobsahuje samotné údaje alebo informácie o tom, ako sa má jednotlivá krajina alebo jazyk reprezentovať.
  • java.util.ResourceBundle
    ResourceBundle je určitým zásobníkom na „lokalizované“ objekty. Celkom jednoducho ak potrebujete niekde vložiť lokalizovaný objekt, zavoláte statickú metódu ResourceBundle.getBundle(„package.MyResource“, Locale). Ako prvé sa vyhľadá zdedená trieda od triedy ResourceBundle, ktorá má meno ukazujúce na špecifikované Locale. Ak sa taká trieda nenájde, začne sa vyhľadávanie tzv. properties súboru (musí sa nachádzať v classpath), ktorého meno rovnako ukazuje na špecifikované Locale. Ak sa nenájde ani properties súbor, je vyhodená výnimka MissingResourceException.
    Z triedy ResourceBundle sú odvodené aj dve ďalšie, a to ListResourceBundle a PropertyResourceBundle. Prvá z nich je abstraktná trieda a jej úlohou je postarať sa o lokalizované zdroje v tvare zoznamu (list). Druhá trieda má na starosti čítanie a poskytovanie textových lokalizovaných zdrojov z properties súborov, pričom často nie je nutné volať túto triedu priamo. Čo sa týka samotného properties súboru, jeho tvar je veľmi dôležitý a musí byť vytvorený podľa tejto schémy: menoZdroja_jazyk_krajina.properties. Meno zdroja musí presne odpovedať prvému parametru v metóde getBundle().
    Takže ak zavoláte getBundle("package.MyBaseName", new Locale("de", "DE")), vyhľadá sa trieda s názvom MyBaseName_de. Ak sa nenájde, bude sa hľadať súbor s názvom MyBaseName_de_DE.properties. Properties súbor je štandardný textový súbor obsahujúci na každom riadku dvojicu „kľúč = hodnota“. Ak tomu zatiaľ nerozumiete, nelámte si hlavu, pochopíte to z príkladov.
  • java.text.Format
    Trieda Format je abstraktným predkom tých tried, ktoré zabezpečujú formátovanie dátumu, času alebo čísiel podľa konkrétneho národného prostredia. Napr. trieda DateFormat je používaná na formátovanie a parsing dátumu a času a trieda NumberFormat zase na tvorbu desatinných oddeľovačov, resp. oddeľovačov tisícou.

Vysvetlime si použitie týchto tried na príkladoch. Majme nejakú vstupnú stránku, na ktorej máme možnosť zvoliť si preferovaný jazyk prostredníctvom radio buttonu. Tu je jej zdrojový kód (na konci článku si máte možnosť stiahnuť všetky zdrojové kódy).

Vstupná stránka pre I18n servlet

Použitý formulár odkazuje na localizedservlet. Pod týmto URL, respektíve menom, máme zaregistrovaný v súbore web.xml servlet interval.LocalizedServlet. Tento servlet používa dve triedy zdedené od ResourceBundle, a to už spomínané ListResourceBundle a PropertyResourceBundle. V nasledujúcom listingu uvádzam len metódu doPost(), pretože logika celého servletu je v podstate v nej (kompletný zdrojový kód servletu).

public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
String param = req.getParameter(„lang“);
String lang = param.substring(0,2).trim();
String country = param.substring(3).trim();
ResourceBundle lres = ResourceBundle.getBundle(
    „interval.ListResource“, new Locale(lang,country));
ResourceBundle pres = ResourceBundle.getBundle(
    „interval.PropertyResource“, new Locale(lang,country));
DateFormat df = DateFormat.getDateInstance(
    DateFormat.FULL, new Locale(lang,country));
String dnes = df.format(new Date());
res.setContentType(CONTENT_TYPE);
PrintWriter out = res.getWriter();
out.println(„<html>“);
out.println(„<head><title>LocalizedServlet</title></head>“);
out.println(„<body bgcolor=\“#ffffff\“>“);
out.println(lres.getString(„welcome“));
out.println(pres.getString(„today“) + “ “ + dnes);
out.println(„<a href=’choose.jsp‘>“+ lres.getString(„back“) + „</a>“);
out.println(„</body></html>“);
}

Prvým krokom je rozdeliť získaný parameter na kód jazyka a kód krajiny. Tieto kódy sa následne využijú pri vytváraní objektu Locale. Nasleduje dynamické vytvorenie inštancie lres. V závislosti od zadaného locale sa použije niektorá z tried ListResource_de, ListResource_en alebo ListResource_sk (ich výpis uvádzam nižšie). Podobne sa vytvorí aj inštancia pres, len s tým rozdielom, že odkazujem na triedu, ktorá zabezpečí načítanie niektorého zo súborov PropertyResource_de.properties, PropertyResource_en.properties alebo PropertyResource_sk.properties, znova podľa zadaného locale. Samotná implementácia tejto triedy je pred programátorom bežne skrytá. Nič vám však nebráni vytvoriť si vlastnú verziu. Trieda cez vstupný prúd bytov načíta jednotlivé hodnoty a kľúče z properties súboru a vloží ich do objektu triedy java.util.Properties.

V ďalšom kroku sa vytvorí objekt nesúci dnešný dátum a pomocou triedy DateFormat a objektu Locale sa tento dátum sformátuje. Prostredníctvom metódy getString(„key“) vložíte do programu hodnotu priradenú k danému kľúču. Ostatné kroky by vám už mali byť jasné. Nasleduje listing jednej z použitých tried obsahujúci lokalizované údaje v nemčine:

public class ListResource_de extends ListResourceBundle {
  private static final Object[][] contents = new String[][]{
      {„welcome“ , „<p><h2>Willkommen in unserem Server</h2></p>“},
      {„back“, „<p>Zurück</p>“}};
  public Object[][] getContents() {
   return contents;
  }
}

Ostatné triedy sú identické, len obsahujú texty v angličtine a slovenčine. Čo sa týka properties súborov, nebudem ich tu všetky vypisovať, ich štruktúru som už vysvetlil. Nájdete ich v balíčku, ktorý si môžete stiahnuť.

Vidíte, je to celkom jednoduché. V reálnej aplikácii (toto bol len umelý príklad) by ste určite vložili objekt Locale do aktuálnej session, aby ste tak zabezpečili správnu podobu všetkých stránok, ktoré budú klientovi odoslané. V tomto článku som síce demonštroval niektoré výhody Java I18n API, ale možností je samozrejme ďaleko viac.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *