Penanganan Locale di Android

Kualifikasi resource dan UX peka locale pada aplikasi Android.

Daftar isi

Aplikasi yang sama dipakai di banyak negara, sehingga angka, tanggal, dan teksnya perlu menyesuaikan pengaturan perangkat. Pengaturan itu disebut locale, yaitu identitas bahasa beserta wilayah seperti id-ID untuk bahasa Indonesia atau en-US untuk Inggris Amerika. Topik ini menguji pengaruh locale terhadap format angka dan tanggal langsung di JVM, lalu membahas cara Android menyajikan teks dalam berbagai bahasa melalui kualifikasi resource.

Kelas Locale di JVM#

Kelas java.util.Locale memodelkan locale pada JVM, dan objeknya dibuat dari kode bahasa plus kode wilayah seperti id-ID. Pemformat angka dan tanggal menerima locale sebagai argumen, sehingga nilai yang sama dapat dicetak dalam bentuk yang berbeda tanpa mengubah datanya. Nilai yang diformat tetap 1500000.5 di dalam program, sedangkan bentuk cetaknya yang berubah.

Apabila pemformat dibuat tanpa argumen locale, JVM memakai locale bawaan yang dapat dibaca melalui Locale.getDefault(). Perilaku itu nyaman untuk program pribadi, tetapi kurang menentu untuk server, karena bentuk keluarannya bergantung pada mesin tempat program berjalan. Server yang melayani banyak negara biasanya memilih locale secara eksplisit untuk tiap permintaan.

kotlin FormatLocale.kt
import java.text.NumberFormat
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.Locale

fun main() {
    val harga = 1500000.5
    val tanggal = LocalDate.of(2026, 11, 17)
    for (kode in listOf("id-ID", "en-US", "de-DE")) {
        val locale = Locale.forLanguageTag(kode)
        val angka = NumberFormat.getNumberInstance(locale).format(harga)
        val hari = tanggal.format(DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy", locale))
        println("$kode : $angka : $hari")
    }
}
bash
# compiler Kotlin dan pustaka standar yang dipakai jalur ini
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi FormatLocale.kt, lalu jalankan hasilnya di JVM
kotlinc FormatLocale.kt -d out.jar && java -cp "out.jar:$STDLIB" FormatLocaleKt
text Keluaran
id-ID : 1.500.000,5 : Selasa, 17 November 2026
en-US : 1,500,000.5 : Tuesday, 17 November 2026
de-DE : 1.500.000,5 : Dienstag, 17 November 2026

Keluaran memperlihatkan tiga bentuk untuk satu nilai. Locale id-ID menuliskan pemisah ribuan dengan titik dan desimal dengan koma, kebalikan dari en-US, sementara de-DE kebetulan berbagi kebiasaan yang sama dengan id-ID. Baris tanggal lebih jelas lagi, yaitu nama hari Selasa, Tuesday, dan Dienstag untuk nilai LocalDate yang sama. Tidak ada satu pun nilai yang diubah di dalam program, karena yang berbeda hanyalah objek Locale yang diberikan kepada pemformatnya.

Kualifikasi resource di Android#

Android menyimpan teks tampilan di berkas resource bernama strings.xml di dalam direktori res/values. Untuk bahasa lain, direktori baru dibuat dengan akhiran kode locale, misalnya values-in untuk bahasa Indonesia, dan isinya memakai nama kunci yang sama. Saat aplikasi berjalan, Android memilih direktori yang cocok dengan locale perangkat, lalu kembali ke values apabila kunci yang dicari tidak ada di sana.

xml strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="salam">Hello!</string>
    <string name="jumlah_item">%d items</string>
</resources>
xml strings-in.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="salam">Halo!</string>
    <string name="jumlah_item">%d item</string>
</resources>

Kedua berkas di atas menempati jalur res/values/strings.xml dan res/values-in/strings.xml di dalam proyek Android. Nama kuncinya dipanggil dari kode, misalnya getString(R.string.salam), sehingga kode aplikasi tidak perlu tahu bahasa apa yang sedang dipakai perangkat. Penambahan bahasa baru pun berhenti di pembuatan direktori resource, tanpa perubahan kode apa pun.

Kode locale hanya satu jenis kualifikasi. Android juga menyediakan kualifikasi untuk mode gelap, ukuran layar, dan keadaan perangkat lain, dan semuanya bekerja dengan pola yang sama, yaitu direktori bernama khusus dipilih oleh sistem saat aplikasi berjalan. Mekanisme ini memisahkan nilai dari kode, sehingga penyesuaiannya tidak menumpuk di dalam program.

Prinsip UX peka locale#

Format yang benar hanya bisa dicapai dengan pemformat yang peka locale, bukan dengan menyambung teks secara manual. Fungsi seperti NumberFormat yang diberi objek locale mengikuti kebiasaan perangkat secara otomatis. Kalimat yang menggabungkan nilai juga sebaiknya disimpan utuh di resource dengan placeholder, misalnya %1$d item, sehingga urutan katanya dapat diatur per bahasa.

Kesabaran pada tata letak juga diperlukan karena panjang teks berubah antarbahasa. Tombol yang muat untuk satu kata di satu bahasa bisa terpotong di bahasa lain, sehingga lebar tetap sebaiknya dihindari pada teks yang berubah-ubah. Sebagian bahasa juga ditulis dari kanan ke kiri, dan Android menyediakan kualifikasi arah untuk itu. Cara mengujinya sederhana, yaitu mengubah bahasa perangkat, lalu menyusuri layar aplikasi yang sama.

Tanggal perlu perlakuan lebih hati-hati lagi, karena menyimpan tanggal sebagai teks terformat merusak perhitungannya. Nilai tanggal sebaiknya disimpan sebagai data murni, misalnya LocalDate atau jumlah milidetik, lalu diformat pada saat ditampilkan. Dengan pola itu, perubahan locale di perangkat langsung diikuti tanpa migrasi data, dan perhitungan selisih hari tetap benar.

Kesalahan umum#

Kesalahan yang paling mahal adalah menyimpan angka atau tanggal yang sudah terformat ke dalam basis data. Nilai 1.500.000,5 yang disimpan sebagai teks tidak dapat dijumlahkan, diurutkan dengan benar, maupun diformat ulang untuk bahasa lain. Contoh nyatanya adalah kolom pencarian yang berhenti menemukan hasil setelah nilai terformat ikut tersimpan sebagai teks. Pengurutan teks juga terganggu, karena urutan abjad yang dianggap benar berbeda antarbahasa, dan JVM menyediakan pembanding khusus bernama Collator untuk itu. Simpan nilai mentahnya di dalam data, dan simpan hanya bentuk terformatnya di layar.

Kesalahan lain adalah mengubah besar kecilnya huruf dengan fungsi lama seperti toUpperCase() tanpa menyadari keterikatannya pada locale bawaan. Pada locale tertentu, misalnya Turki, aturan pemetaan hurufnya berbeda sehingga hasilnya tidak terduga. Kotlin menyediakan uppercase() tanpa argumen yang memakai aturan Unicode yang tetap, dan pilihan itu lebih aman untuk kode yang tidak terikat bahasa tertentu. Aturan yang mudah diingat, yaitu fungsi yang punya varian dengan parameter Locale hampir selalu lebih aman dibandingkan varian tanpa parameter.

Ringkasan#

  • Locale mengidentifikasi bahasa beserta wilayahnya dan menentukan bentuk angka, tanggal, serta nama hari yang ditampilkan.
  • Pada JVM, kelas java.util.Locale beserta NumberFormat dan DateTimeFormatter mengubah tampilan nilai yang sama sesuai locale yang diberikan.
  • Android menyimpan teks di strings.xml per kualifikasi, misalnya values untuk bawaan dan values-in untuk bahasa Indonesia, dengan nama kunci yang sama.
  • Kalimat yang memuat nilai disimpan lengkap dengan placeholder di resource, dan tata letaknya harus menampung perbedaan panjang teks antarbahasa.

Referensi#