Spolu s menu nám v předchozím článku přibylo několik textů a jejich náhodné roztroušení po zdrojovém kódu začíná být poněkud nepřehledné. Navíc, chceme-li aplikaci mít ve více než jednom jazyku, je načase se s texty vypořádat trochu jinak.

Více jazyků najednou?

Na tuto otázku odpovím rovnou záporně. Texty mohou zabrat přibližně 2 až 8 kB, což je u některých telefonů a aplikací příliš mnoho na to, abychom tuto velikost zabírali dvakrát nebo dokonce i vícekrát. Také je velmi málo pravděpodobné, že by na jednom telefonu bylo potřeba mít zároveň k dispozici více jazyků, a je tedy zvykem, mít při stahování aplikace na výběr z jednotlivých jazykových verzí.

Kam s texty?

Při nakládání s texty máme zhruba tyto možnosti umístění:

  • přímo v kódu
  • ve speciální třídě s textovými konstantami
  • v deskriptoru aplikace
  • ve speciálním souboru jako text
  • ve speciálním souboru jako binární data

Každá z nich má své výhody a nevýhody, takže si je dále rozebereme trochu podrobněji. Podíváme se, jak je u jednotlivých přístupů lehké či komplikované změnit nějaký text nebo převést aplikaci do jiného jazyka.

Přímo v kódu

Tento způsob práce s texty jsme používali dosud, protože je nejjednodušší a tedy se při psaní programu přímo sám nabízí. U aplikací, které mají podporovat více jazyků nebo jsou rozsáhlejší, však tato metoda není vůbec vhodná.

Abychom převedli aplikaci do jiného jazyka, museli bychom najít všechny textové řetězce roztroušené všude ve zdrojovém kódu, nahradit je jejich překladem a aplikaci znovu přeložit. I kvůli změně jednoho řetězce bychom museli prohledat zdrojové kódy, už jen proto, abychom tento řetězec našli, a překladu aplikace se také nevyhneme. Zde je vidět, že je obecně lepší mít všechny textové konstanty na jednom místě.

Ve speciální třídě s textovými konstantami

public class Texts {
  public static final String BACK = „Zpět“;
  public static final String HELP = „Nápověda“;
  public static final String HELP_CONTENT = „Šipkami jezděte …“;
  public static final String PLAY = „Hrát“;
  public static final String EXIT = „Konec“;
}

Toto je vylepšené předchozí řešení, kdy máme texty stále ve zdrojovém kódu, ale jsou umístěny všechny v jedné třídě, která slouží pouze jako skladiště textových konstant.

Nyní můžeme třídu obsahující konstanty vyrobit v několika verzích – co jazyk, to verze. Jazyk aplikace tak bude určen tím, která verze bude do aplikace zabalena.

Nevýhodou zůstává, že ke změně textu je potřeba znovu přeložit a sestavit aplikaci, takže tuto činnost musí dělat někdo, kdo „ví, co dělá“.

V deskriptoru aplikace

Jednoznačnou výhodou ukládání textů do deskriptoru aplikace je, že mechanismus na jejich načítání už J2ME obsahuje a nemusíme jej tedy programovat. Dále však následují i nevýhody:

  1. Různé telefony mají různě velikou maximální povolenou velikost deskriptoru aplikace.
  2. Délka jednotlivých řádků je omezená a některé telefony se neumí vypořádat se zalamovanými řádky deskriptoru aplikace.
  3. České znaky je potřeba ukládat v kódování UTF-8.
  4. I přes aplikaci předchozího bodu na některých telefonech některé české znaky nefungují.

Rozhodnete-li se přesto ukládat texty do deskriptoru, doporučuji udělat si předem průzkum, jak se k nim budou chovat cílové telefony.

Změnu textu nebo překlad aplikace do jiného jazyka může s texty umístěnými v deskriptoru provádět prakticky kdokoli, což zjednodušuje práci a umožňuje její dělbu.

Ve speciálním souboru jako text

Přenesením textů aplikace do speciálního souboru, který bude uložen uvnitř JAR souboru spolu s aplikací, se zbavíme většiny problémů, které jsou spojeny s ukládáním textů do deskriptoru aplikace, zůstane pouze poslední bod předchozího výčtu. Navíc ale budeme muset sami vyřešit načtení textů.

Pro jednoduchost a přehlednost zvolíme stejný způsob ukládání textů, jako má deskriptor aplikace. Každý řádek bude obsahovat dvojici klíč: hodnota dělenou dvojtečkou. Načtení takto strukturovaného souboru do java.util.Hashtable předvádí následující ukázka:

Hashtable texts = new Hashtable();
InputStream is = null;
InputStreamReader isr = null;
try {
  // otevření zdrojového souboru
  is = this.getClass().getResourceAsStream(„/texts.txt“);
  // vytvoření instance InputStreamReader se zadaným kódováním UTF-8
  isr = new InputStreamReader(is, „UTF-8“);
  int i = isr.read();
  StringBuffer buf = new StringBuffer();
  String key = null;
  while (i != -1) {
    // zpracování dat, dokud nenastane konec souboru
    char ch = (char) i;
    if (ch != ‚\n‘ && (ch != ‚:‘ || key != null)) {
      // načítání klíče nebo jeho hodnoty
      buf.append(ch);
    } else if (ch == ‚:‘ && key == null) {
      // zpracování dvojtečky oddělující klíč
      // od jeho hodnoty
      key = buf.toString();
      buf.delete(0, buf.length());
    } else if (ch == ‚\n‘) {
      // zpracování konce řádku
      if (key != null) {
        texts.put(key, buf.toString());
        key = null;
      }
      buf.delete(0, buf.length());
    }
    // načtení dalšího znaku
    i = isr.read();
  }
  // není-li poslední řádek ukončen znakem nového řádku
  // je potřeba uložit poslední text zde
  if(key!=null){
    texts.put(key, buf.toString());
  }
} catch (Exception e) {
} finally {
  if(isr!=null){
    // zavření instance InputStreamReader
    try{
      isr.close();
    } catch(Exception e){
    }
  }
  if(is!=null){
    // zavření instance InputStream
    try{
      is.close();
    } catch(Exception e){
    }
  }
}

V aplikaci pak uložíme všechny klíče jako konstanty do třídy GameMIDlet a této třídě také přidáme metodu getText(String klíč), která vrátí hodnotu přiřazenou danému klíči.

Ve speciálním souboru jako binární data

Při tomto způsobu uložení dat jsem na žádné problémy s češtinou zatím nenarazila. To ovšem neznamená, že takto můžete použít libovolné znaky. Třeba jednoduchá uvozovka na Nokii 7650 neměla šanci.

Drobnou komplikací je, že lidsky čitelný soubor s texty je potřeba zkonvertovat do binárního formátu, ale jak jste si už asi všimli, ruční ošetřování všech odlišností pro jednotlivá zařízení a jazykové verze by bylo při tvorbě více aplikací příliš úmorné a poskytovalo by mnoho možností vzniku chyby. Proto se v budoucnu nějaké automatizaci chtě nechtě nevyhneme.

Ke konverzi textového souboru do binárního nám bude sloužit pomocná třída TextsToBin. Načtení textového souboru pro jednoduchost uděláme stejně jako u předchozího příkladu. Uložení textů v binární podobě vypadá takto:

FileOutputStream fos = null;
DataOutputStream dos = null;
try{
  // vytvoření výstupního proudu do souboru
  fos = new FileOutputStream(outName);
  // vytvoření datového výstupního proudu,
  // který data posílá dál do
  // souborového výstupního proudu
  dos = new DataOutputStream(fos);
  Enumeration enum = texts.keys();
  // zapsání počtu textů
  dos.writeInt(texts.size());
  while(enum.hasMoreElements()){
    // uložení klíče a pak hodnoty textu
    String key = (String)enum.nextElement();
    dos.writeUTF(key);
    dos.writeUTF((String)texts.get(key));
  }
  } catch(Exception e){
    e.printStackTrace();
  } finally {
    if(dos!=null){
      // zavření datového výstupního proudu
      try{
        dos.close();
      } catch(Exception e){
    }
  }
    if(fos!=null){
      // zavření souborového výstupního proudu
      try{
        fos.close();
      } catch(Exception e){
    }
  }
}

Načtení textů v midletu je obdobné, jen se místo výstupních proudů používají vstupní proudy a místo zapisování texty čteme.

Hashtable texts = new Hashtable();
InputStream is = null;
DataInputStream dis = null;
try {
  // vytvoření vstupního proudu ze souboru
  is = this.getClass().getResourceAsStream(„/texts.bin“);
  // vytvoření datového vstupního proudu, který
  // čte data z výše vytvořeného vstupního proudu
  dis = new DataInputStream(is);
  // načtení počtu textů
  int i = dis.readInt();
  while(i>0){
    // načtení textu
    texts.put(dis.readUTF(), dis.readUTF());
    i–;
  }
} catch (Exception e) {
} finally {
  if(dis!=null){
    // zavření datového vstupního proudu
    try{
      dis.close();
    } catch(Exception e){
    }
  }
  if(is!=null){
    // zavření vstupního proudu
    try{
      is.close();
    } catch(Exception e){
    }
  }
}

Co zvolit?

Odpověď na tuto otázku závisí na tom, kolik aplikací budete vyrábět, kolik a jaká zařízení chcete podporovat a zda plánujete podporu více jazyků nebo nikoli. Pokud si píšete jednu aplikaci pro radost, po které chcete, aby běhala jen na vašem telefonu, klidně si pište texty někam do kódu a netrapte se obsahem tohoto článku. Máte-li větší ambice, budete muset zvolit některý z komplikovanějších přístupů. A třeba vymyslíte nějaké vlastní geniální řešení, na které jsem ještě nepřišla.

Ke stažení

Veškeré zde uvedené zdrojové kódy, obrázky ke hře a hotovou aplikaci si můžete stáhnout a použít pro vlastní potřebu.

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

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

Žádný příspěvek v diskuzi

Odpovědět