Internasjonalisering og lokalisering i Java 8

1. Oversikt

Internasjonalisering er en prosess for å utarbeide en applikasjon for å støtte ulike språklige, regionale, kulturelle eller politiske spesifikke data. Det er et viktig aspekt av enhver moderne flerspråklig applikasjon.

For videre lesing, vi burde vite at det er en veldig populær forkortelse (sannsynligvis mer populær enn selve navnet) for internasjonalisering - i18n på grunn av de 18 bokstavene mellom ‘i’ og ‘n’.

Det er avgjørende for dagens bedriftsprogrammer å betjene mennesker fra forskjellige deler av verden eller flere kulturområder. Distinkte kultur- eller språkregioner bestemmer ikke bare språkspesifikke beskrivelser, men også valuta, tallrepresentasjon og til og med avvikende dato- og tidssammensetning.

La oss for eksempel fokusere på landsspesifikke tall. De har forskjellige desimaler og tusen skilleark:

  • 102 300,00 (USA)
  • 102 300,45 (Polen)
  • 102.300,45 (Tyskland)

Det finnes også forskjellige datoformater:

  • Mandag 1. januar 2018 15:20:34 CET (USA)
  • lundi 1. januar 2018 15 t 20 CET (Frankrike).
  • 2018 年 1 月 1 日 星期一 下午 03 时 20 分 34 秒 CET (Kina)

I tillegg har forskjellige land unike valutasymboler:

  • £ 1,200,60 (Storbritannia)
  • € 1.200,60 (Italia)
  • 1 200,60 € (Frankrike)
  • $ 1,200,60 (USA)

Et viktig faktum å vite er at selv om land har samme valuta og valutasymbol - som Frankrike og Italia - kan posisjonen til valutasymbolet være annerledes.

2. Lokalisering

Innen Java har vi en fantastisk funksjon til vår disposisjon kalt Lokal klasse.

Det lar oss raskt skille mellom kulturelle lokaliteter og formatere innholdet vårt riktig. Det er viktig for i internasjonaliseringsprosessen. Det samme som i18n, har også Localization sin forkortelse - l10n.

Hovedårsaken til bruk Lokal er at all nødvendig stedsspesifikk formatering er tilgjengelig uten rekompilering. En applikasjon kan håndtere flere lokaliteter samtidig, så det er enkelt å støtte nytt språk.

Lokalbefolkningen er vanligvis representert med forkortelse for språk, land og variant atskilt med en understreking:

  • de (tysk)
  • it_CH (italiensk, Sveits)
  • no_US_UNIX (USA, UNIX-plattform)

2.1. Enger

Det har vi allerede lært Lokal består av språkkode, landskode og variant. Det er to mulige felt å angi: skript og utvidelser.

La oss se gjennom en liste over felt og se hva reglene er:

  • Språk kan være en ISO 639 alpha-2 eller alpha-3 kode eller registrert språkundermerk.
  • Region (Land) er ISO 3166 alfa-2 landskode eller FN-tall-3 retningsnummer.
  • Variant er en skiller mellom store og små bokstaver eller verdisett som spesifiserer en variant av a Lokal.
  • Manus må være gyldig ISO 15924 alfa-4 kode.
  • Utvidelser er et kart som består av enkelttegntaster og String verdier.

IANA Language Subtag Registry inneholder mulige verdier for Språk, region, variant og manus.

Det er ingen liste over mulige Utvidelse verdier, men verdiene må være velformede BCP-47 delmerker. Tastene og verdiene konverteres alltid til små bokstaver.

2.2. Lokalitet.Bygger

Det er flere måter å skape Lokal gjenstander. En mulig måte utnyttes Lokalitet.Bygger. Lokalitet.Bygger har fem settermetoder som vi kan bruke til å bygge objektet og samtidig validere disse verdiene:

Lokal locale = ny Locale.Builder () .setLanguage ("fr") .setRegion ("CA") .setVariant ("POSIX") .setScript ("Latn") .build ();

De String representasjon av ovennevnte Lokal er fr_CA_POSIX_ # Latn.

Det er godt å vite det å sette ‘variant’ kan være litt vanskelig, da det ikke er noen offisiell begrensning på variantverdier, selv om settermetoden krever at den er BCP-47 kompatibel.

Ellers vil det kaste Misdannet Lokaleksepsjon.

I tilfelle der vi trenger å bruke en verdi som ikke består validering, kan vi bruke Lokal konstruktører da de ikke validerer verdier.

2.3. Konstruktører

Lokal har tre konstruktører:

  • ny språk (streng språk)
  • new Locale (String språk, String land)
  • ny lokal (streng språk, streng land, streng variant)

En 3-parameter konstruktør:

Lokal språk = ny lokal ("pl", "PL", "UNIX");

En gyldig variant må være en String med 5 til 8 alfanumeriske eller enkeltnumre etterfulgt av 3 alfanumeriske. Vi kan bare bruke "UNIX" på variant felt bare via konstruktør, da det ikke oppfyller disse kravene.

Imidlertid er det en ulempe med å bruke konstruktører til å lage Lokal objekter - vi kan ikke angi utvidelser og skriptfelt.

2.4. Konstanter

Dette er trolig den enkleste og mest begrensede måten å komme seg på Lokalbefolkningen. De Lokal klassen har flere statiske konstanter som representerer det mest populære landet eller språket:

Lokal japan = Locale.JAPAN; Lokal japansk = Lokal.JAPANSK;

2.5. Språkmerker

En annen måte å skape på Lokal kaller den statiske fabrikkmetoden forLanguageTag (String språkTag). Denne metoden krever en String som møter IETF BCP 47 standard.

Slik kan vi skape Storbritannia Lokal:

Locale uk = Locale.forLanguageTag ("en-UK");

2.6. Tilgjengelige lokaliteter

Selv om vi kan lage flere kombinasjoner av Lokal gjenstander, kan det hende at vi ikke kan bruke dem.

En viktig merknad å være klar over er at Lokalbefolkningen på en plattform er avhengig av de som er installert i Java Runtime.

Som vi bruker Lokalbefolkningen for formatering kan de forskjellige formaterne ha et enda mindre sett med Lokalbefolkningen tilgjengelige som er installert i Runtime.

La oss sjekke hvordan du henter matriser med tilgjengelige lokaler:

Lokal [] numberFormatLocales = NumberFormat.getAvailableLocales (); Lokal [] dateFormatLocales = DateFormat.getAvailableLocales (); Locale [] locales = Locale.getAvailableLocales ();

Etter det kan vi sjekke om vår Lokal bor blant tilgjengelige Lokalbefolkningen.

Vi burde huske det settet med tilgjengelige lokaler er forskjellig for forskjellige implementeringer av Java-plattformenog ulike funksjonsområder.

Den komplette listen over støttede lokaliteter er tilgjengelig på Oracles Java SE Development Kit-webside.

2.7. Standard lokalitet

Mens vi arbeider med lokalisering, må vi kanskje vite hva som er standard Lokal på vår JVM eksempel er. Heldigvis er det en enkel måte å gjøre det på:

Lokal standardLocale = Locale.getDefault ();

Vi kan også spesifisere en standard Lokal ved å kalle en lignende settermetode:

Locale.setDefault (Locale.CANADA_FRENCH);

Det er spesielt relevant når vi vil lage JUnit tester som ikke avhenger av a JVM forekomst.

3. Tall og valutaer

Denne delen refererer til tall- og valutaformaterere som skal være i samsvar med forskjellige stedsspesifikke konvensjoner.

For å formatere primitive talltyper (int, dobbelt) samt deres objektekvivalenter (Heltall, Dobbelt), bør vi bruke NumberFormat klasse og dens statiske fabrikkmetoder.

To metoder er interessante for oss:

  • NumberFormat.getInstance (lokal språk)
  • NumberFormat.getCurrencyInstance (locale sted)

La oss undersøke en eksempelkode:

Lokal usLocale = Locale.US; dobbelt tall = 102300.456d; NumberFormat usNumberFormat = NumberFormat.getInstance (usLocale); assertEquals (usNumberFormat.format (nummer), "102,300,456");

Som vi kan se er det så enkelt som å lage Lokal og bruke den til å hente NumberFormat forekomst og formatere et eksempelnummer. Det kan vi merke produksjonen inkluderer lokalspesifikk desimal og tusen skilletegn.

Her er et annet eksempel:

Lokal usLocale = Locale.US; BigDecimal nummer = nytt BigDecimal (102_300.456d); NumberFormat usNumberFormat = NumberFormat.getCurrencyInstance (usLocale); assertEquals (usNumberFormat.format (nummer), "$ 102 300,46");

Formatering av en valuta innebærer de samme trinnene som formatering av et tall. Den eneste forskjellen er at formateringen legger valutasymbol og rund desimaldel til to sifre.

4. Dato og tid

Nå skal vi lære om datoer og klokkeslettformatering som sannsynligvis er mer komplisert enn formatering av tall.

Først og fremst bør vi vite at dato og klokkeslettformatering endret seg betydelig i Java 8, siden den inneholder helt ny Dato tid API. Derfor skal vi se gjennom forskjellige formateringsklasser.

4.1. DateTimeFormatter

Siden Java 8 ble introdusert, er hovedklassen for lokalisering av datoer og tider DateTimeFormatter klasse. Den opererer på klasser som implementeres TemporalAccessor grensesnitt, for eksempel, LocalDateTime, LocalDate, LocalTime eller ZonedDateTime. Å lage en DateTimeFormatter vi må gi minst et mønster, og da Lokal. La oss se en eksempelkode:

Locale.setDefault (Locale.US); LocalDateTime localDateTime = LocalDateTime.of (2018, 1, 1, 10, 15, 50, 500); Strengmønster = "dd-MMMM-åååå HH: mm: ss.SSS"; DateTimeFormatter defaultTimeFormatter = DateTimeFormatter.ofPattern (mønster); DateTimeFormatter deTimeFormatter = DateTimeFormatter.ofPattern (mønster, lokalitet.GERMANY); assertEquals ("01-januar-2018 10: 15: 50.000", defaultTimeFormatter.format (localDateTime)); assertEquals ("01-Januar-2018 10: 15: 50.000", deTimeFormatter.format (localDateTime));

Det kan vi se etter henting DateTimeFormatter alt vi trenger å gjøre er å ringe format() metode.

For bedre forståelse, bør vi gjøre oss kjent med mulige mønsterbokstaver.

La oss se på bokstaver for eksempel:

Symbol Betydning Presentasjonseksempler ------ ------- ------------ ------- y år-til-år 2004; 04 M / L måned / årstall / tekst 7; 07; Jul; Juli; J d månedsdag nummer 10 H time-of-day (0-23) nummer 0 m minutt-til-time-nummer 30 s sekund-for-minutt-nummer 55 S brøkdel av sekund-fraksjon 978

Alle mulige mønsterbokstaver med forklaring finner du i Java-dokumentasjonen til DateTimeFormatter.Det er verdt å vite at den endelige verdien avhenger av antall symboler. Det er 'MMMM' i eksemplet som skriver ut hele månedsnavnet, mens en enkelt 'M' bokstav vil gi månedstallet uten ledende 0.

For å fullføre DateTimeFormatter, la oss se hvordan vi kan formatere LocalizedDateTime:

LocalDateTime localDateTime = LocalDateTime.of (2018, 1, 1, 10, 15, 50, 500); ZoneId losAngelesTimeZone = TimeZone.getTimeZone ("America / Los_Angeles"). ToZoneId (); DateTimeFormatter localizedTimeFormatter = DateTimeFormatter .ofLocalizedDateTime (FormatStyle.FULL); Streng formattedLocalizedTime = localizedTimeFormatter.format (ZonedDateTime.of (localDateTime, losAngelesTimeZone)); assertEquals ("Mandag 1. januar 2018 10:15:50 PST", formatLocalizedTime);

For å formatere LocalizedDateTime, kan vi bruke ofLocalizedDateTime (FormatStyle dateTimeStyle) metode og gi en forhåndsdefinert FormatStyle.

For en mer grundig titt på Java 8 Dato tid API, vi har en eksisterende artikkel her.

4.2. DateFormat og SimpleDateFormatter

Siden det fremdeles er vanlig å jobbe med prosjekter som bruker Datoer og Kalendere, vil vi kort introdusere muligheter for formatering av datoer og klokkeslett med DateFormat og SimpleDateFormat klasser.

La oss analysere evnene til den første:

GregorianCalendar gregorianCalendar = ny GregorianCalendar (2018, 1, 1, 10, 15, 20); Dato dato = gregorianCalendar.getTime (); DateFormat ffInstance = DateFormat.getDateTimeInstance (DateFormat.FULL, DateFormat.FULL, Locale.ITALY); DateFormat smInstance = DateFormat.getDateTimeInstance (DateFormat.SHORT, DateFormat.MEDIUM, Locale.ITALY); assertEquals ("giovedì 1 febbraio 2018 10.15.20 CET", ffInstance.format (dato)); assertEquals ("01/02/18 10.15.20", smInstance.format (dato));

DateFormat fungerer med Datoer og har tre nyttige metoder:

  • getDateTimeInstance
  • getDateInstance
  • getTimeInstance

Alle tar forhåndsdefinerte verdier av DateFormat som parameter. Hver metode er overbelastet, så forbigående Lokal er mulig også. Hvis vi vil bruke et tilpasset mønster, slik det gjøres i DateTimeFormatter, Vi kan bruke SimpleDateFormat. La oss se en kort kodebit:

GregorianCalendar gregorianCalendar = ny GregorianCalendar (2018, 1, 1, 10, 15, 20); Dato dato = gregorianCalendar.getTime (); Locale.setDefault (ny lokal ("pl", "PL")); SimpleDateFormat fullMonthDateFormat = ny SimpleDateFormat ("dd-MMMM-åååå HH: mm: ss: SSS"); SimpleDateFormat shortMonthsimpleDateFormat = ny SimpleDateFormat ("dd-MM-åååå HH: mm: ss: SSS"); assertEquals ("01-lutego-2018 10: 15: 20: 000", fullMonthDateFormat.format (dato)); assertEquals ("01-02-2018 10: 15: 20: 000", shortMonthsimpleDateFormat.format (dato));

5. Tilpasning

På grunn av noen gode designbeslutninger er vi ikke bundet til et stedsspesifikt formateringsmønster, og vi kan konfigurere nesten alle detaljer for å være fullt fornøyde med utdataene.

For å tilpasse nummerformatering kan vi bruke Desimalformat og DesimalFormatSymboler.

La oss vurdere et kort eksempel:

Locale.setDefault (Locale.FRANCE); BigDecimal nummer = nytt BigDecimal (102_300.456d); DecimalFormat zeroDecimalFormat = ny DecimalFormat ("000000000.0000"); DecimalFormat dollarDecimalFormat = ny DecimalFormat ("$ ###, ###. ##"); assertEquals (zeroDecimalFormat.format (nummer), "000102300,4560"); assertEquals (dollarDecimalFormat.format (nummer), "$ 102 300,46"); 

De Desimalformat dokumentasjonen viser alle mulige mønstertegn. Alt vi trenger å vite nå er at "000000000.000" bestemmer ledende eller etterfølgende nuller, ',' er tusen skilletegn, og '.' er desimal.

Det er også mulig å legge til et valutasymbol. Vi kan se nedenfor at det samme resultatet kan oppnås ved å bruke DateFormatSymbol klasse:

Locale.setDefault (Locale.FRANCE); BigDecimal nummer = nytt BigDecimal (102_300.456d); DesimalFormatSymboler desimalFormatSymbols = DecimalFormatSymbols.getInstance (); desimalFormatSymbols.setGroupingSeparator ('^'); desimalFormatSymbols.setDecimalSeparator ('@'); DecimalFormat separatorsDecimalFormat = new DecimalFormat ("$ ###, ###. ##"); separatorsDecimalFormat.setGroupingSize (4); separatorsDecimalFormat.setCurrency (Currency.getInstance (Locale.JAPAN)); separatorsDecimalFormat.setDecimalFormatSymbols (desimalFormatSymbols); assertEquals (separatorsDecimalFormat.format (nummer), "$ 10 ^ [e-postbeskyttet]");

Som vi kan se, DesimalFormatSymboler klasse kan vi spesifisere hvilken som helst tallformatering vi kan forestille oss.

Å tilpasse SimpleDataFormat, Vi kan bruke DateFormatSymbols.

La oss se hvor enkelt det er å bytte dagsnavn:

Dato dato = ny gregoriansk kalender (2018, 1, 1, 10, 15, 20) .getTime (); Locale.setDefault (ny lokal ("pl", "PL")); DateFormatSymbols dateFormatSymbols = nye DateFormatSymbols (); dateFormatSymbols.setWeekdays (ny streng [] {"A", "B", "C", "D", "E", "F", "G", "H"}); SimpleDateFormat newDaysDateFormat = ny SimpleDateFormat ("EEEE-MMMM-åååå HH: mm: ss: SSS", dateFormatSymbols); assertEquals ("F-lutego-2018 10: 15: 20: 000", newDaysDateFormat.format (dato));

6. Ressurspakker

Til slutt, den avgjørende delen av internasjonalisering i JVM er den Ressurspakke mekanisme.

Formålet med en ResourceBundle er å tilby et program med lokaliserte meldinger / beskrivelser som kan eksternaliseres til de separate filene. Vi dekker bruk og konfigurasjon av ressurspakken i en av våre tidligere artikler - guide til ressurspakken.

7. Konklusjon

Lokalbefolkningen og formatørene som bruker dem er verktøy som hjelper oss med å lage en internasjonalisert applikasjon. Disse verktøyene lar oss lage et program som dynamisk kan tilpasse seg brukerens språklige eller kulturelle innstillinger uten å bygge flere eller til og med trenger å bekymre seg for om Java støtter Lokal.

I en verden der en bruker kan være hvor som helst og snakke hvilket som helst språk, betyr muligheten til å bruke disse endringene våre applikasjoner kan være mer intuitive og forståelige for flere brukere globalt.

Når vi arbeider med Spring Boot-applikasjoner, har vi også en praktisk artikkel for Spring Boot Internationalization.

Kildekoden til denne opplæringen, med fullstendige eksempler, finner du på GitHub.