7.1 Internationale programmer 122
7.1.1 Formatering af tidspunkter 122
7.1.2 Formatering af tal og beløb 123
7.1.3 Tekstindhold i resursefiler 123
7.1.4 Avanceret: Binære resursefiler 124
7.1.5 Avanceret tekstformatering 125
7.2 Selv bestemme sproget 127
7.2.1 Styre sproget fra kommandolinjen 127
7.2.2 De mulige Locale-objekter 127
7.2.3 Bruge Locale-objekter 128
7.2.4 Avanceret: Sætte standardsprog 128
Når et program skal anvendes af flere kulturer og sprog, opstår behov for, at programtekster, beløb og dato angives i de pågældende landes sprog, og man må i gang med at internationalisere og lokaliseret programmet.
Internationalisering (eng.: Internationalization, også kaldet I18N) består i at gøre programmet sprogneutralt, ved at sørge for at al formatering og fortolkning af tal-, beløbs-, dato- og tidsangivelser sker afhængigt af sproget, og at al sproglig tekst er flyttet til resursefiler.
Lokalisering består i at oversætte resursefilerne til et bestemt sprog.
DateFormat formaterer Date-objekter til strenge (og den anden vej).
import java.text.*; import java.util.*; public class BenytDateFormat { public static void main(String arg[]) { DateFormat klformat, datoformat, dkf; klformat = DateFormat.getTimeInstance(DateFormat.MEDIUM); datoformat = DateFormat.getDateInstance(DateFormat.FULL); dkf = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.SHORT); Date tid = new Date(); System.out.println( tid ); System.out.println( "Kl :"+ klformat.format(tid) ); System.out.println( "Dato :"+ datoformat.format(tid) ); System.out.println( "Tid :"+ dkf.format(tid) ); } }
Wed Feb 05 14:23:46 GMT+00:00 2003 Kl :14:23:46 Dato :5. februar 2003 Tid :05-02-2003 14:23
Læg for det første mærke til, at toString() på Date ikke er lokaliseret. Den bør kun bruges til test-udskrifter og logning og ikke i tekst, som brugeren skal læse.
Ovenstående program er kørt med danske sprogindstillinger. Med amerikanske sprogindstillinger bliver uddata i stedet:
Mon Dec 03 13:27:57 GMT+01:00 2001
Kl: 1:27:57 PM
Dato: Monday, December 3, 2001
Tid: Dec 3, 2001 1:27 PM
Det ses, at det afhænger en del af sproget, præcist hvordan tider bliver formateret (f.eks. er ugedag med i den amerikanske tekst, men ikke i den danske).
Ønsker man som programmør fuld kontrol over, hvordan teksten bliver formateret, må man selv specificere formatet med SimpleDateFormat:
import java.text.*; import java.util.*; public class BenytSimpleDateFormat { public static void main(String arg[]) { DateFormat df = new SimpleDateFormat("EEEE 'den' d. MMMM 'år' yyyy."); Date tid = new Date(); System.out.println( df.format(tid) ); } }
mandag den 3. december år 2001.
Dermed bliver selve formaterings-strengen sprogspecifik (og den bør lægges ud i en resursefil - se senere). Køres den med amerikanske sprogindstillinger, bliver uddata:
Monday den 3. December år 2001.
På samme måde som med tidsangivelser formateres/fortolkes tal ved at bede om et formateringsobjekt, der klarer netop denne form for tal:
import java.text.*; public class BenytNumberFormat { public static void main(String arg[]) { NumberFormat fmt1 = NumberFormat.getInstance(); NumberFormat fmt2 = NumberFormat.getCurrencyInstance(); NumberFormat fmt3 = NumberFormat.getPercentInstance(); double tal = 1234.5678; System.out.println( fmt1.format(tal) ); System.out.println( fmt2.format(tal) ); System.out.println( fmt3.format(tal) ); } }
1.234,568 kr 1.234,57 123.457%
Med amerikanske sprogindstillinger bliver uddata:
1,234.568 $1,234.57 123,457%
Programmet BenytDateFormat er halvt internationaliseret, da tidsformateringen korrekt skifter afhængigt af sproget. Der mangler kun strengene "Kl: ", "Dato: " og "Tid: ".
Lad os nu fuldføre internationaliseringen ved at lægge tekstindholdet (strengene) ud i et resursebundt (eng.: resource bundle) med navnet Tekster. Disse kan tilgås fra programmet således:
import java.text.*; import java.util.*; public class BenytDateFormatMedResurser { public static void main(String arg[]) { ResourceBundle res = ResourceBundle.getBundle("Tekster"); DateFormat klformat, datoformat, dkf; klformat = DateFormat.getTimeInstance(DateFormat.MEDIUM); datoformat = DateFormat.getDateInstance(DateFormat.FULL); dkf = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.SHORT); Date tid = new Date(); System.out.println( res.getString("Kl_")+ klformat.format(tid) ); System.out.println( res.getString("Dato_")+ datoformat.format(tid) ); System.out.println( res.getString("Tid_")+ dkf.format(tid) ); } }
Kl : 14:23:50 Dato: 5. februar 2003 Tid : 05-02-2003 14:23
Resursebundtet, der skal ligge i filen Tekster.properties, indeholder teksten i nøgle-værdi-par. Disse vil blive brugt, hvis der ikke findes resursefiler for det pågældende sprog:
Tid_=Tid \: Kl_=Kl \: Dato_=Dato \:
Køres programmet (på et styresystem indstillet til dansk), fås samme udskrift som før.
Lad os nu lokalisere programmet til engelsk. Det består i, at vi opretter Tekster_en.properties med de engelske tekster:
Tid_=Time \: Kl_=Time of day\: Dato_=Date \:
Starter vi derefter programmet med engelske sprogindstillinger, får vi uddata:
Time of day : 3:07:28 PM Date : Monday, December 3, 2001 Time : Dec 3, 2001 3:07 PM
Når programmet skal finde en programtekst ud fra sprogindstillingerne, sker det ved først at kigge i den mest specifikke resursefil. Hvis programteksten ikke findes der, kigges i en mere generel resursefil og til sidst i den mest generelle.
For eksempel vil resurser til sproget fr_CA (canadisk fransk) blive søgt
først i Tekster_fr_CA.properties
dernæst i Tekster_fr.properties
og sidst i Tekster.properties
Ved opstart af et program opretter Java et Locale-objekt, der svarer til sprogindstillingerne på maskinen.
På et UNIX-system gøres det ud fra miljøvariablen LC_CTYPE eller LC_ALL. Den amerikanske udskrift til BenytDateFormat er f.eks. fundet med UNIX-kommandoen:
LC_CTYPE=en java BenytDateFormat
Man kan undersøge, hvilke Locale-objekter der er tilgængelige, ved at kalde klassemetoden Locale.getAvailableLocales():
import java.text.*; import java.util.*; public class MuligeSprog { public static void main(String arg[]) { Locale[] l = Locale.getAvailableLocales(); for (int i=0; i<l.length; i++) System.out.print(l[i]+" "); System.out.println( ); } }
ar ar_AE ar_BH ar_DZ ar_EG ar_IQ ar_JO ar_KW ar_LB ar_LY ar_MA ar_OM ar_QA ar_SA
ar_SD ar_SY ar_TN ar_YE be be_BY bg bg_BG ca ca_ES cs cs_CZ da da_DK de de_AT
de_CH de_DE de_LU el el_GR en_AU en_CA en_GB en_IE en_IN en_NZ en_ZA es es_BO
es_AR es_CL es_CO es_CR es_DO es_EC es_ES es_GT es_HN es_MX es_NI es_PA es_PE
es_PR es_PY es_SV es_UY es_VE et et_EE fi fi_FI fr fr_BE fr_CA fr_CH fr_FR fr_LU
hr hi_IN hr_HR hu hu_HU is is_IS it it_CH it_IT iw iw_IL ja ja_JP ko ko_KR lt
lt_LT lv lv_LV mk mk_MK nl nl_BE nl_NL no no_NO no_NO_NY pl pl_PL pt pt_BR pt_PT
ro ro_RO ru ru_RU sh sh_YU sk sk_SK sl sl_SI sq sq_AL sr sr_YU sv sv_SE th th_TH
th_TH_TH tr tr_TR uk uk_UA zh zh_CN zh_HK zh_TW en en_US
Localet består af tre dele:
Første del er sprogkoden, f.eks. da, sv, no, en, fr.
En valgfri anden del er landekoden, f.eks. DK, GB, DE, FR
En valgfri tredje del er varianten inden for sprogområdet (f.eks. om valutaen er i euro).
Eksempler:
fr_BE: Fransk i Belgien
fr_BE_EURO: Fransk i Belgien med euro-valuta
fr_CA: Fransk i Canada
fr_FR: Fransk i Frankrig
fr_LU: Fransk i Luxembourg
Ønsker man finkornet kontrol over, hvilket sprog der anvendes, kan man oprette et Locale-objekt selv, og dette objekt kan så bruges til at fremskaffe formateringsobjekter til det pågældende sprog:
import java.text.*; import java.util.*; public class BenytDateFormat2 { public static void main(String arg[]) { DateFormat klformat, datoformat; Locale fransk = new Locale("fr","FR"); klformat = DateFormat.getTimeInstance(DateFormat.SHORT,fransk); datoformat = DateFormat.getDateInstance(DateFormat.LONG, fransk); Date tid = new Date(); System.out.println( "Kl: "+ klformat.format(tid) ); System.out.println( "Dato: "+ datoformat.format(tid) ); } }
Kl: 14:23
Dato: 5 février 2003