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"> <items> <MenuItem text="%menunewcaption" /> </items> </Menu> 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 = 1E Lze vynutit nevýznamové nuly, např. pro 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 DEFAULT 4.3.2007 Mar 4, 2007 SHORT 4.3.07 3/4/07 MEDIUM 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 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Í