Java Internationalization/Localization
Uygulamaların farklı ülkelerde ve farklı dillerde çalışması gerekebilir. Bu kapsamda, internationalization; uygulamaların yazılımın çeşitli dillerde ve bölgelerde oluşabilecek mühendislik gerektirecek değişiklikler yapılmadan tasarlanma sürecidir. Localization ise; birden çok yerel ayarı veya coğrafi bölgeyi desteklemek anlamına gelmektedir. Bir yerel ayarı; bir dil ve ülke eşleşmesi gibi düşünebiliriz. Localization, stringleri farklı dillere çevirmeyi içerir. Ayrıca bu yerel ayar için doğru bir biçimde çıktı tarih ve sayılar içerir.
Locale sınıfı; java.util paketi altındadır. Kendi yerel ayarlarımızı aşağıdaki gibi bulabiliriz. Çıktı olarak tr_TR ifadesini verdi. Küçük harfle yazılmış tr ifadesi bilgisayarı Türkçe kullandığımı ifade eder. Büyük harflerle yazılmış TR ifadesi ise Türkiye’de bulunduğumu söylemektedir. Büyük harflerle yazılan bölge kodu isteğe bağlıdır; ancak küçük harflerle yazılan dil kodunun bulunması zorunludur.
Locale locale = Locale.getDefault(); System.out.println(locale);
Varsayılandan farklı bir yerel ayar ile kod yazmamız gerekebilir. Bunun için Locale sınıfındaki yerleşik sabitler kullanılmaktadır. Örneğin; aşağıdaki kodda, FRENCH sabiti kullanılarak sadece fransızca dil seçildi, FRANCE sabiti kullanılarak hem dil hem bölge ayarı seçilmiş oldu.
System.out.println(Locale.FRENCH); //fr System.out.println(Locale.FRANCE); //fr_FR
Bir yerel ayar seçmenin ikinci yolu yeni bir nesne oluşturmak için constructorları kullanmaktır. Bu yöntemle yalnızca bir dili veya hem dili hem ülkeyi seçebiliriz: Bu yöntemle 2. satırda da olduğu gibi yerel ayar için bir uyumsuzluk oluşturabilir; görüldüğü üzere dil-bölge uyumu yok.
System.out.println(new Locale("tr")); //tr System.out.println(new Locale("tr","EN")); //tr_EN
Daha esnek bir yerel ayar oluşturmak için bir 3. yolumuz daha var. Bu durum da aşağıdaki gibidir:
Locale a = new Locale.Builder() .setLanguage("en") .setRegion("EN") .build();
Bir uygulamayı test ederken kullanığımız yerel ayar dışında bir yerel ayar ile test etmek isteyebiliriz. Bunu da aşağıdaki gibi sağlayabiliriz:
System.out.println(Locale.getDefault()); //tr_TR Locale loc = new Locale("en"); Locale.setDefault(loc); System.out.println(Locale.getDefault()); //en
Numaraları Yerelleştirme
Para değeri ve sayı değerlerini formatlarken ya da parse ederken bu değerler bulunduğumuz yerel ayara göre değişebilir. Örneğin, Amerika’da dolar küsüratları nokta ile ayrılırken; Almanya’da euro küsüratları virgül ile ayrılmaktadır. Bu tip sorunlara engel olmak için java.text paketi altında kullanışlı sınıflar bulunmaktadır. Verileri formatlanın veya parse etmenin ilk adımı aynıdır. NumberFormat sınıfının bir örneğini alırız :
Normal bir formatter | NumberFormat.getInstance(locale) |
Para birimi için formatter | NumberFormat.getCurrencyInstance(locale) |
Yüzdeler için formatter | NumberFormat.getPercentInstance(locale) |
Ondalık değerleri yuvarlamak için formatter | NumberFormat.getIntegerInstance(locale) |
NumberFormat örneğine sahip olduğumuzda, bir sayıyı Stringe dönüştürmek için format() metodunu çağırabilir veya bir String’i sayıya dönüştürmek için parse() metodunu kullanabiliriz.
format() metodunun NumberFormat örneğinden oluşturulmuş değeri String’e dönüştürdüğünü söylemiştik. Aşağıdaki örnekte, bir ülkedeki yıllık doğum sayısını baz alarak, aylık ortalama doğum sayısının ülkelere göre yerelleştirerek ifade edilmesinin bir örneği görülmektedir. Doğum ücreti olarak 75 birim para alındığını varsayarsak, bunun Almanya için yerelleştirerek euro cinsinden ücretini gösterebiliriz.
Para birimleri ile ilgili işlem yaparken sakın double kullanma 🙂 Para işlemlerinde int ya da BigDecimal kullan.
//Format işlemi int dogumSayisi = 5_600_000; int ortalamaAyDogumSayisi = dogumSayisi / 12; var amerika = NumberFormat.getInstance(Locale.US); System.out.println(amerika.format(ortalamaAyDogumSayisi)); // 466,666 var almanya = NumberFormat.getInstance(Locale.GERMANY); System.out.println(almanya.format(ortalamaAyDogumSayisi)); // 466.666 var fransa = NumberFormat.getInstance(Locale.FRENCH); System.out.println(fransa.format(ortalamaAyDogumSayisi)); // 466 666 double ucret = 75; var locale = NumberFormat.getCurrencyInstance(Locale.GERMANY); System.out.println(locale.format(ucret)); // 75,00 €
parse() metodu bir String’i yapılandırılmış bir nesneye ya da primitive bir değere dönüştürür. NumberFormat.parse() ile bunu sağlarız ve yerel ayarı kullanırız. Parse işlemi ile ilgili özel bir checked exception olan ParseException istisnası bulunmaktadır. Parse işlemini kodumuza uyguladığımızda derleyici ParseExceptionu eklememizi isteyecektir.
Aşağıda parse metodu örneklendirilmiştir. Amerika’da nokta sayılarda kullanılmaktadır. Doğal olarak parse ederken, sayının hepsini getirdi. Ancak Fransa’da sayılar arasında nokta kullanılmaz; bu sebeple noktayı formatlama karakteri olarak alacak ve ona göre parse işlemini yapacak ve sayının geri kalanına bakmayı bırakacaktır.
public static void main(String[] args) throws ParseException { String k = "23.76"; var ingiliz = NumberFormat.getInstance(Locale.US); System.out.println(ingiliz.parse(k)); //23.76 var fransiz = NumberFormat.getInstance(Locale.FRANCE); System.out.println(fransiz.parse(k)); //23 }
Parse işlemini para birimleri için de yaparız. Aşağıda amerika yerel ayarıyla dolar birimi parse edildi. Çıkan sonuç bir Number objesidir.
public static void main(String[] args) throws ParseException { String ucret = "$12,233.99"; var uc = NumberFormat.getCurrencyInstance(Locale.US); double deger = (Double) uc.parse(ucret); System.out.println(deger); //12233.99 }
Kendi özel numara formatlarımızı yapmak isteyebiliriz. Bunun için DecimalFormat sınıfını kullanacağız. Bunun için kendi tasarım kalıbımızı kullanmamız gerekecek. En sık kullanılan tasarım kalıpları aşağıdaki gibidir:
# : Virgüle kadar tasarım kalıbını uygular; uymayan kısımları atlar, virgülden sonrasını yuvarlar.
0 : Tasarım kalıbını tamamen uygular, boş kısımlara sıfır koyar.
Bunu örnekle açıklayalım:
double k = 3242334234.4343; NumberFormat n1 = new DecimalFormat("###,###,###.0"); System.out.println(n1.format(k)); //3.242.334.234,4 NumberFormat n2 = new DecimalFormat("000,000,000.00000"); System.out.println(n2.format(k)); //3.242.334.234,43430
Tarihleri Yerelleştirme
Tarihlerde, sayılar gibi yerelleştirilebilir. Bunun için DateTimeFormatter sınıfı kullanılır. Bunun için DateTimeFormatter sınıfının ofLocalizedDate, ofLocalizedDateTime ve ofLocalizedTime metodları kullanılmaktadır. Aşağıda kullanımı örneklendirilmiştir:
public class FormatLocalize { public static void main(String[] args) { Locale.setDefault(new Locale("tr","TR")); var fr = new Locale("fr","FR"); var dt = LocalDateTime.of(2021,Month.JANUARY,19,11,33,34); yaz(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT),dt,fr); //19.01.2021, 19/01/2021 yaz(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT,FormatStyle.SHORT),dt,fr); //19.01.2021 11:33, 19/01/2021 11:33 } public static void yaz(DateTimeFormatter f,LocalDateTime dt, Locale loc) { System.out.println(f.format(dt)+", "+f.withLocale(loc).format(dt)); } }
Locale.setDefault() metodunu kullandığımızda, Java ‘da görüntüleme ve formatlama amacıyla Locale.Category adında kullanışlı bir formatlama ve görüntüleme enumu sağlamaktadir. Bu enumdan en sık kullanılanı; DISPLAY ve FORMAT’tır. Aşağıdaki örnekte görüldüğü gibi bu enum sayesinde, dil ayarını DİSPLAY ile, bölgeye özgü formatlamayı FORMAT enum değeri ile gerçekleştirebildik.
public class LocalCategory { public static void main(String[] args) { var tr = new Locale("tr","TR"); var para = 12.56; Locale.setDefault(new Locale("en","US")); yaz(tr,para); //$12.56, Turkish Locale.setDefault(Category.DISPLAY,tr); yaz(tr,para); //$12.56, Türkçe Locale.setDefault(Category.FORMAT,tr); yaz(tr,para); //?12.56, Türkçe } public static void yaz(Locale loc, double para) { System.out.println(NumberFormat.getCurrencyInstance().format(para)+", "+loc.getDisplayLanguage()); } }