Java a národní prostředí RICHARD LIPKA 25.4.2016
Nastavení národního prostředí Vstup a výstup speciálních znaků, zpracování řetězců (velká písmena, řazení)
Formátování výstupu (čísla, datum, měna) Oddělení národnostních zvyklostí od zdrojového textu ◦ Konfigurační soubory / služby OS ◦ Soubory s překládanými texty ◦ S FXML a CSS snadné úpravy vzhledu okna
Pojmy: ◦ Internacionalizace: přizpůsobení programu národnímu prostředí ◦ Lokalizace: převedení programu do jiné jazykové mutace
25.4.2016
UUR - I18N, LOKALIZACE
2
Třída Locale Základ pro práci s národním prostředím Ovlivňuje mnoho dalších tříd (zejména spojených s výstupem)
Lze vytvořit instanci, kterou ostatní používají → pak je snadné prostředí přepnout ◦ Ve VM vždy nastavena jedna instance jako výchozí
Informace o jazyku a oblasti (souhrnně lokalita) Statická metoda Locale.getDefault() vrací aktuální nastavení JVM ◦ Shoduje se s nastavením OS ◦ Ve Windows Start – Ovládací panely – Oblast a jazyk (nastavení jazyka, formát data, času, desetinné čárky, …)
Vytvoření konstruktorem:
new Locale(String jazyk, String zeme) czLocale = new Locale("cs", "CZ");
25.4.2016
UUR - I18N, LOKALIZACE
3
Třída Locale – ukázka výstupu public class MojeLocale { public static void main(String[] args) throws Exception { Locale d = Locale.getDefault(); System.out.println("Země : " + d.getCountry()); System.out.println("Jazyk: " + d.getLanguage()); System.out.println("Země : " + d.getDisplayCountry()); System.out.println("Jazyk: " + d.getDisplayLanguage()); System.out.println("ISO země : " + d.getISO3Country()); System.out.println("ISO jazyk: " + d.getISO3Language()); }
} Země : CZ Jazyk: cs Země : Česká republika Jazyk: čeština ISO země : CZE ISO jazyk: ces
25.4.2016
UUR - I18N, LOKALIZACE
4
Třída Locale – použití void setDefault(Locale newLocale) ◦ Nastaví novou lokalitu pro JVM (ne pro OS)
Locale[] getAvailableLocales() ◦ Nalezení podporovaných prostředí pro objekty schopné pracovat s Locale (např. DateFormat) ◦ Čeština („cs_CZ“) a Slovenština (sk_SK) fungují spolehlivě
Objekty Locale jen identifikátory, samy nic nedělají (slouží jako parametr pro metody pracující s daty)
25.4.2016
UUR - I18N, LOKALIZACE
5
Lokalizace Pro snadný převod programu do jiného jazyka Využívají se .properties soubory
Příklad pro překlad: public class NotI18N { static public void main(String[] args) { System.out.println("Hello."); System.out.println("How are you?"); System.out.println("Goodbye.");
}
}
25.4.2016
UUR - I18N, LOKALIZACE
6
ResourceBundle Nejsnazší (a „nejčistší“) řešení překladu Je nutné připravit několik .properties souborů ◦ Nutné dodržet názvy, defaultní: MessagesBundle.properties
Soubor MessagesBundle_de_DE.properties: # FOR GERMAN ... tohle je komentář ... následují páry klíč = hodnota greetings = Hallo. farewell = Tschüß. inquiry = Wie geht's?
Soubor MessagesBundle_fr_FR.properties: greetings = Bonjour. farewell = Au revoir. inquiry = Comment allez-vous?
25.4.2016
UUR - I18N, LOKALIZACE
7
Program s ResourceBundle public class I18NSample { static public void main(String[] args) { String language, country; if (args.length != 2) { language = new String(“fr"); country = new String(“FR"); } else { language = new String(args[0]); country = new String(args[1]); } Locale currentLocale = new Locale(language, country); ResourceBundle messages = ResourceBundle.getBundle("MessagesBundle",currentLocale); System.out.println(messages.getString("greetings")); System.out.println(messages.getString("inquiry")); System.out.println(messages.getString("farewell")); } }
25.4.2016
UUR - I18N, LOKALIZACE
8
FXML s ResourceBundle V FXML lze definovat které řetězce mají být nahrazeny (jakýkoliv řetězec začínající % bude chápán jako klíč do bundlu)
<Menu text="%menuFileCaption">
<MenuItem text="%menuNewCaption" /> Loaderu je třeba předat i bundle s texty texts = ResourceBundle.getBundle(TEXT_FILE, Locale.getDefault()); FXMLLoader loader = new FXMLLoader (getClass().getResource(MAIN_WINDOW_XML), texts); rootPane = (Pane)loader.load();
25.4.2016
UUR - I18N, LOKALIZACE
9
Proměnné údaje Umožňuje pracovat s měnícími se texty zpráv v .properties souborech Podpora ve tříde MessageFormat, metoda format()
Příklad z tutorialu: # .properties soubor template = At {2,time,short} on {2,date,long}, we detected \ {1,number,integer} spaceships on the planet {0}. planet = Mars Object[] argumentyZpravy = { messages.getString("planet"), new Integer(7), new Date() }; String input = messages.getString("template"); String output = MessageFormat.format(zprava, argumentyZpravy);
25.4.2016
UUR - I18N, LOKALIZACE
10
Kódování Existuje několik kódů pro znaky ◦ Anglické znaky v ASCII které ostatní přebírají bez problémů ◦ Diakritika často různá, podle národa
Java vnitřně pracuje v Unicode UTF-16 (2B na znak) Operační systémy mohou používat různá kódování ◦ Je třeba zařídit převod při načítání souborů, zpracování znaků z konzole, ve výstupech a při použití fontů ◦ Převod musí fungovat oběma směry
Java podporuje několik kódování ◦ Zavedené zkratky v API
25.4.2016
UUR - I18N, LOKALIZACE
11
Kódování – podporované kódy ISO-8859-2 (ISO LATIN2) ◦ Mezinárodní norma, obvyklé v Unixu ◦ V Javě občas jako ISO8859_2
windows-1250 (CP-1250) ◦ Proprietární kódování MS, implicitně v JDK pod Windows ◦ Od ISO-8859-2 se liší jen v několika znacích (hlavně š, Š, ť, Ť, ž, Ž) ◦ V Javě jako Cp1250
IBM 852 (DOS Latin2, PC Latin2, CP 852) ◦ V konzoli českých Windows (XP, 7), umí „rámečkovou grafiku“ ◦ V Javě jako Cp852
Unicode - UTF-8 ◦ Implementace Unicode, proměnná délka znaku ◦ Nativní kódování v novějších distribucích Linuxu ◦ V Javě jako UTF8
25.4.2016
UUR - I18N, LOKALIZACE
12
Čeština v programu Jména identifikátorů ◦ Překladač je musí správě rozpoznat, očekává implicitní kódování OS ◦ Převádí vše na UTF-8
◦ Implicitně (v OS Windows) ve windows-1250 (totéž řetězce), záleží na textovém editoru (při změně překódovat, ne jen přehodit zobrazení!)
Texty výpisů ◦ Překlad bez problémů, nekontroluje kódování ◦ Konzole Windows v IBM 825 → špatné akcentované znaky (JRE používá pro výstup defaultní kódování - windows-1250 ) (řešení za chvilku)
V .class souborech UTF-8 → jsou bez problémů přenositelné na jiné OS
25.4.2016
UUR - I18N, LOKALIZACE
13
Čeština v programu - příklad public class CestinaIdentifikatory { public static void main(String[] args) { int pěknýČeskýČítač = 1; System.out.println("pěknýČeskýČítač =„ + pěknýČeskýČítač); } }
25.4.2016
UUR - I18N, LOKALIZACE
14
Převod zdrojového textu Pokud zdrojový text není v implicitním kódování (přenesen z jiného OS, nevhodně nastavený editor), bude problém s překladem
Při překladu předchozího příkladu v UTF-8:
Kódování lze změnit v externím editoru nebo prostředky JDK
25.4.2016
UUR - I18N, LOKALIZACE
15
JDK -encoding Přepínač javac.exe, rozpozná základní kódování
Nemění vstupní soubor Soubor v UTF-8 nesmí být uložen s Byte Order Mark (BOM) – odlišení endianů ◦ Endian – pořadí uložení bytů vícebitových slov (UTF-16 znak, Int32, …)
25.4.2016
UUR - I18N, LOKALIZACE
16
JDK native2ascii.exe Detekce kódování obtížná, pro distribuci do neznámého prostředí je lepší převést do jednotné podoby
Převod souborů na čisté ASCII ◦ Nahrazení akcentovaných znaků escape sekvencí 'ě' – '\u011b‚ za \u je číslo znaku v Unicode ◦ Paramentr –encoding pro jiná než nativní kódování zdrojových textů ◦ Výstup implicitně do konzole, lze nasměrovat do souboru (rourou nebo parametrem) ◦ Použít pomocný soubor
25.4.2016
UUR - I18N, LOKALIZACE
17
JDK native2ascii.exe příklad
25.4.2016
UUR - I18N, LOKALIZACE
18
JDK native2ascii.exe -reverse S převedeným souborem se špatně pracuje Převod lze i obrátit, přepínač –reverse ◦ escape sekvence převede na znaky ve zvoleném kódování
25.4.2016
UUR - I18N, LOKALIZACE
19
Čeština v konzoli Při spouštění z Eclipse nebo jiného IDE se problém neobjeví, jejich „konzole“ je ve windows-1250 (nativním kódování OS)
Stejné principy platí i pro práci se soubory Základem využití tříd InputStreamReader a OutputStreamWriter ◦ Umí převádět kódování mezi 8b znaky OS a 16b znaky Javy ◦ Implicitně používají nativní kódování OS ◦ Lze změnit druhým parametrem konstruktoru
25.4.2016
UUR - I18N, LOKALIZACE
20
Čeština v konzoli - příklad public class NaKonzoli { public static void main(String[] args) throws Exception { OutputStreamWriter oswDef = new OutputStreamWriter(System.out); System.out.println("Implicitni kodovani konzole: " + oswDef.getEncoding()); /* IBM852 je výstupní kódování češtiny v konzoli */ OutputStreamWriter osw = new OutputStreamWriter(System.out, "IBM852"); System.out.println("Nastavene kodovani konzole: " + osw.getEncoding()); PrintWriter p = new PrintWriter(osw); p.print("Příšerně žluťoučký kůň úpěl ďábelské ódy.\n"); p.print("áčďéěíňóřšťúůýž\n"); p.print("PŘÍŠERNĚ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY.\n"); p.print("ÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ\n"); p.flush();
} }
25.4.2016
UUR - I18N, LOKALIZACE
21
Vstup češtiny z konzole Podobné jako u výstupu Dvě základní možnosti ◦ Pomocí InputStreamReader (pro vstup ze souborů) ◦ Pomocí Scanner (pro vstup z klávesnice)
(u vstupu z GUI není s kódováním obvykle žádný problém)
25.4.2016
UUR - I18N, LOKALIZACE
22
Vstup češtiny z konzole příklad public class IOKonzole { public static void main(String[] args) throws Exception { OutputStreamWriter osw = new OutputStreamWriter(System.out, "IBM852"); PrintWriter p = new PrintWriter(osw); InputStreamReader isr = new InputStreamReader(System.in, "IBM852"); BufferedReader ib = new BufferedReader(isr); Scanner sc = new Scanner(System.in, "IBM852"); p.print("Zadej akcentované znaky: "); p.flush(); String s = sc.nextLine(); p.print("Zadal jsi: " + s + "\n"); p.flush(); p.print("Zadej další akcentované znaky: "); p.flush(); s = ib.readLine(); p.print("Zadal jsi: " + s + "\n"); p.flush(); } }
25.4.2016
UUR - I18N, LOKALIZACE
23
Čeština v souborech Stejný problém jako při práci s konzolí (obojí je „jen stream“) Třídy Reader a Writer konvertují znaky podle implicitního kódování ◦ Systémová vlastnost file.encoding (nikdy neměnit) ◦ Zjištění System.getProperty("file.encoding")
Třídy InputStreamReader a OutputStreamWriter dokáží zpracovat libovolné kódování
25.4.2016
UUR - I18N, LOKALIZACE
24
Čeština v souborech - čtení public class SouborCteni { public static void main(String[] args) throws Exception { OutputStreamWriter osw = new OutputStreamWriter(System.out, "IBM852"); PrintWriter p = new PrintWriter(osw); String kodovani = args[0]; FileInputStream fis = new FileInputStream("vstup.utf8"); InputStreamReader isr = new InputStreamReader(fis, kodovani); BufferedReader br = new BufferedReader(isr); String radka; while ((radka = br.readLine()) != null) { p.println(radka); p.flush(); } fis.close();
} }
25.4.2016
UUR - I18N, LOKALIZACE
25
Čeština v souborech - čtení
25.4.2016
UUR - I18N, LOKALIZACE
26
Čeština v souborech - zápis public class SouborZapis { public static void main(String[] args) throws Exception { String kodovani = args[0]; String jmenoSouboru = "vystup." + kodovani; FileOutputStream fos = new FileOutputStream(jmenoSouboru); OutputStreamWriter osw = new OutputStreamWriter(fos, kodovani); PrintWriter p = new PrintWriter(osw); String kun = "Příšerně žluťoučký kůň úpěl ďábelské ódy"; String akcenty = "áčďéěíňóřšťúůýž"; p.println(kun); p.println(akcenty); p.println(kun.toUpperCase()); p.println(akcenty.toUpperCase()); p.close();
} }
25.4.2016
UUR - I18N, LOKALIZACE
27
Převod kódování uvnitř programu Pokud je třeba získat řetězec v určitém kódování uvnitř programu ◦ byte[] getBytes(String kodovani) ve třídě String ◦ Vrací pole bajtů ve zvoleném kódování
Existuje konstruktor pro opačný postup ◦ String(byte[] bytes, String kodovani)
Múže se hodit při práci se soubory s definovaným kódováním (třeba při načítání .dbf souborů starých databází)
25.4.2016
UUR - I18N, LOKALIZACE
28
Převod kódování - příklad public class PrevodStringu { public static String byteNaHexa(byte[] b) { String s = " "; for (int i = 0; i < b.length; i++) { int j = (b[i] < 0) ? 256 + b[i] : b[i]; s = s + s.format("%02x ", j); } return s; } public static void main(String[] args) throws Exception { String test = "áčďéěíňóřšťúůýž"; String[] kodovani = {"windows-1250", "ISO-8859-2", "IBM852", "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16"}; for (int i = 0; i < kodovani.length; i++) { byte[] b = test.getBytes(kodovani[i].trim()); System.out.println(kodovani[i] + ":" + byteNaHexa(b)); } } }
25.4.2016
UUR - I18N, LOKALIZACE
29
Převod kódování - příklad windows-1250: e1 e8 ef e9 ec ed f2 f3 f8 9a 9d fa f9 fd 9e ISO-8859-2 : e1 e8 ef e9 ec ed f2 f3 f8 b9 bb fa f9 fd be IBM852 : a0 9f d4 82 d8 a1 e5 a2 fd e7 9c a3 85 ec a7 UTF-8 : c3 a1 c4 8d c4 8f c3 a9 c4 9b c3 ad c5 88 c3 b3 c5 99 c5 a1 c5 a5 c3 ba c5 af c3 bd c5 be UTF-16BE : 00 e1 01 0d 01 0f 00 e9 01 1b 00 ed 01 48 00 f3 01 59 01 61 01 65 00 fa 01 6f 00 fd 01 7e UTF-16LE : e1 00 0d 01 0f 01 e9 00 1b 01 ed 00 48 01 f3 00 59 01 61 01 65 01 fa 00 6f 01 fd 00 7e 01 UTF-16 : fe ff 00 e1 01 0d 01 0f 00 e9 01 1b 00 ed 01 48 00 f3 01 59 01 61 01 65 00 fa 01 6f 00 fd 01 7e
25.4.2016
UUR - I18N, LOKALIZACE
30
Řazení String.compareTo() neumí správně zpracovat akcentované znaky (řadí podle ordinárních hodnot znaků)
Třída java.text.Collator spolupracuje s Locale a umí řadit podle národního prostředí ◦ Česká norma řadí nejdřív podle neakcentovaných znaků, pak akcentovaných a pak podle velikosti písmen
Pro řazení je nutné využít vlastní komparátor, založení na Collator ◦ Lze využívat v kolekcích a metodě Arrays.sort()
25.4.2016
UUR - I18N, LOKALIZACE
31
Příklad použití komparátoru class CeskyAbecedniComparator implements Comparator<String> { private Collator ceskyCol = Collator.getInstance( new Locale("cs", "CZ")); public int compare(String s1, String s2) { return ceskyCol.compare(s1, s2); }
} … Arrays.sort(pole, new CeskyAbecedniComparator()); …
25.4.2016
UUR - I18N, LOKALIZACE
32
Formátování výstupu Základní třídy: ◦ NumberFormat – pro čísla a měny ◦ DecimalFormat – speciální formát pro celá a reálná čísla ◦ DateTimeFormatter – datum a čas
Metoda format() ve třídách PrintStream a String pro usnadnění ◦ Pokud nestačí System.out.println() ◦ Podobná jako výpis v jazyce C ◦ Šířka výpisu, zarovnání, nevýznamové mezery … (hlavně pro zpracování čísel) String String.format(parametr) PrintStream.format() ( System.out.format() )
25.4.2016
UUR - I18N, LOKALIZACE
33
Formátování výstupu – formátovací řetězec Základní formátování výstupu – String.format() Jako první parametr formátovací řetězec, pak proměnné s daty ◦ Parametrem může být i Locale, pokud se má použít místo aktuálního
Za znakem % formátovací znaky Kolik je znaků % tolik musí být parametrů (kromě %n – nová řádka)
Př.: String.format("i = %d, j = %d%n", i, j);
25.4.2016
UUR - I18N, LOKALIZACE
34
Formátování výstupu – celé číslo Používá se %d, např. pro int i = -1234; String.format("i = %d%n", i); // i = -1234
Počet míst lze stanovit, pak se doplňují mezery zleva, tj. zarovnání doprava, např.: String.format("i = %7d%n", i); // i = -1234
Počet míst lze stanovit a zarovnat doleva (zbylé místo se doplní mezerami), např.: String.format("i = %-7dahoj%n", i); // i = -1234 ahoj
Lze vynutit výpis i + znaménka, např. pro int i = 1234; String.format("i = %+7d%n", i); // i = +1234
Vynutí se výpis nevýznamových nul, např.: String.format("i = %07d%n", i); // i = -001234
25.4.2016
UUR - I18N, LOKALIZACE
35
Formátování výstupu – celé číslo, různé soustavy Osmičková soustava, např. pro int j = 30; String.format("j = %o%n", j); // j = 36
Šestnáctková soustava, např. pro int j = 30; String.format("j = %X%n", j); // j = 1E
Počet míst lze určit, např.: String.format("j = %3X%n", j); // j =
Lze vynutit nevýznamové nuly, např. pro
1E
int j = 10;
String.format("j = %02X%n", j); // j = 0A
25.4.2016
UUR - I18N, LOKALIZACE
36
Formátování výstupu – výpis znaku Používá se %c, např. pro char c = 'a'; String.format("c = %c%n", c); // c = a
Lze použít přetypování a lze vypsat více proměnných najednou String.format("Znak %c ma ASCII hodnotu: %d%n", c, (int) c);
Výstup: // Znak a ma ASCII hodnotu: 97
25.4.2016
UUR - I18N, LOKALIZACE
37
Formátování výstupu – reálné číslo Výpis jako běžné reálné číslo %f, např. pro double d = 1234.567; String.format("d = %f%n", d); // d = 1234,567000 ◦ Desetinný oddělovač je závislý na lokalitě – pro ČR je to čárka, nikoliv tečka.
Výpis ve vědeckotechnické notaci %g, např. pro double d = 1234.567; String.format("d = %g%n", d); // d = 1.234567e+03 ◦ Desetinný oddělovač je tečka.
Lze nastavit počet míst celkem (10) a počet míst za desetinným oddělovačem (1), číslo bude zaokrouhleno String.format("d = %10.1f%n", d); // d = 1234,6 ◦ Lze použít zarovnání doleva, výpis nevýznamových nul, oddělovač řádů apod. stejně, jako u celého čísla
25.4.2016
UUR - I18N, LOKALIZACE
38
Formátování výstupu – výpis řetězce Používá se %s, např. pro String s = "Ahoj lidi"; String.format("s = %s%n", s); // s = Ahoj lidi
Řetězec lze vypsat velkými písmeny String.format("s = %S%n", s); // s = AHOJ LIDI
Lze stanovit šířku výpisu, výpis bude zarovnán doprava String.format("s = |%11s|%n", s); // s = | Ahoj lidi|
Výpis lze zarovnat i doleva String.format("s = |%-11s|%n", s); // s = |Ahoj lidi |
25.4.2016
UUR - I18N, LOKALIZACE
39
Formátování– využití lokality format() využívá defaultní lokalitu (Locale objekt) Loklaita se dá zadat jako parametr String.format(lokalita, řetězec, parametry)
Nastavuje ◦ ◦ ◦ ◦
Oddělovač desetinných míst a řádů u čísel Oddělovače a označení dnů a měsíců ve výpisu data U měny značku a jméno měny …
Další detaily k formátovacímu řetězci v tutorialech
25.4.2016
UUR - I18N, LOKALIZACE
40
Formátování – načtení reálného čísla public class CislaLocaleCteni { public static void main(String[] args) throws Exception { Locale[] lo = { new Locale("cs", "CZ"), new Locale("en", "US") }; Scanner sc = new Scanner(System.in).useLocale(lo[1]); System.out.print ("Zadej realne cislo (1,234,567.89): " ); double d = sc.nextDouble(); System.out.println(d); sc = new Scanner(System.in).useLocale(lo[0]); System.out.print ("Zadej realne cislo (1234567,89): " ); d = sc.nextDouble(); System.out.println(d); } }
25.4.2016
UUR - I18N, LOKALIZACE
41
Formátování – načtení reálného čísla, příklad vstupu vypíše: Zadej realne cislo (1,234,567.89): 98,765.432 98765.432 Zadej realne cislo (1234567,89): 98765,432 98765.432
25.4.2016
UUR - I18N, LOKALIZACE
42
Formátování měny Třída java.text.NumberFormat ◦ Získat instanci pomocí getCurrencyInstance() ◦ Pak lze použít metodu format() ◦ Při výpisu je zohledněn potřebný počet desetinných míst
Příklad: nf = NumberFormat.getCurrencyInstance(locale);
String s = nf.format(d); System.out.println(s + "\t" + lo[i].getDisplayName()); 1 234 567,89 Kč čeština (Česká republika) $1,234,567.89 English (United States) 1.234.567,89 € German (Germany)
25.4.2016
UUR - I18N, LOKALIZACE
43
Formátování data Velmi odlišné konvence podle lokality Třída java.time.format.DateTimeFormatter ◦ Sada továrních metod pro specifikaci formátu ◦ ofLocalizetDate() … - nastavení podle předem připravených stylů (většinou stačí) ◦ ofPattern() – nastavení podle formátovacího řetězce Česká republika
USA
4.3.2007
Mar 4, 2007
4.3.07
3/4/07
4.3.2007
Mar 4, 2007
LONG
4. březen 2007
March 4, 2007
FULL
Neděle, 4. březen 2007
Sunday, March 4, 2007
DEFAULT
SHORT MEDIUM
25.4.2016
UUR - I18N, LOKALIZACE
44
Formátování času Opět třída DateTimeFormatter ◦ tovární metoda ofLocalizedTime() – přednastavené styly ◦ ofPattern() – nastavení podle formátovacího řetězce ◦ Užitečné: DEFAULT – 19:53:20 SHORT – 19:53
Pro datum a čas lze využít i formátovací řetězec ◦ Lze si přesně nastavit co se vypisuje a v jakém pořadí ◦ Nastavení lokality ovlivní jen jména měsíců a dní ◦ Lze použít pro objekty tříd LocalTime a LocalDateTime
25.4.2016
UUR - I18N, LOKALIZACE
45
Děkuji za pozornost OTÁZKY? NÁZORY, PŘIPOMÍNKY … ? PŘÍŠTĚ: LOKALIZACE, NÁRODNÍ PROSTŘEDÍ