Header <locale.h> memungkinkan program C beradaptasi dengan preferensi bahasa dan wilayah pengguna—pemisah desimal, urutan tanggal, simbol mata uang, hingga aturan collation string. Tanpa <locale.h>, program C berjalan di locale "C" (juga disebut POSIX), yaitu format Anglo-Saxon: titik sebagai pemisah desimal, nama hari/bulan dalam bahasa Inggris, collation berbasis kode ASCII. Dengan setlocale, kita dapat beralih ke locale pengguna agar output lebih natural. Pada chapter ini kita akan meninjau setlocale, kategori seperti LC_COLLATE/LC_NUMERIC, serta localeconv untuk membaca konfigurasi lokal.
setlocale dan Konstanta Kategori #
Fungsi setlocale(category, locale) mengubah pengaturan locale. Parameter category menentukan apa yang diubah: LC_ALL (semua kategori), LC_COLLATE (perbandingan string), LC_CTYPE (klasifikasi karakter), LC_NUMERIC (format angka), LC_TIME (format tanggal), atau LC_MONETARY (format mata uang). Parameter locale adalah nama locale seperti "en_US.UTF-8" atau "id_ID.UTF-8"; string kosong "" berarti “pakai locale dari environment” (variabel LANG/LC_ALL di shell).
#include <stdio.h>
#include <locale.h>
int main(void) {
/* Default locale: "C" - format Anglo-Saxon */
printf("=== Locale default (\"C\") ===\n");
printf("Decimal point: %f\n", 1234.5);
printf("Number : %'.2f\n", 1234567.89); /* ' flag untuk grouping */
/* Set locale ke environment user */
char *hasil = setlocale(LC_ALL, "");
printf("\n=== setlocale(LC_ALL, \"\") ===\n");
printf("Hasil : %s\n", hasil ? hasil : "(gagal)");
/* Tampilkan perubahan LC_NUMERIC */
struct lconv *lc = localeconv();
printf("Decimal point: '%s'\n", lc->decimal_point);
printf("Thousands sep: '%s'\n", lc->thousands_sep);
printf("Monetary sym : '%s'\n", lc->currency_symbol);
/* Set kategori berbeda-beda */
setlocale(LC_COLLATE, "C");
setlocale(LC_TIME, "C");
printf("\nLC_COLLATE di-set ke \"C\", LC_TIME di-set ke \"C\"\n");
return 0;
}Output yang Dihasilkan:
=== Locale default ("C") ===
Decimal point: 1234.500000
Number : 1234567.89
=== setlocale(LC_ALL, "") ===
Hasil : LC_CTYPE=C.UTF-8;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=C;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=C;LC_IDENTIFICATION=C
Decimal point: '.'
Thousands sep: ''
Monetary sym : ''
LC_COLLATE di-set ke "C", LC_TIME di-set ke "C"Mari kita telaah baris-baris kode di atas:
- Baris 7 (
printf("... %f\n", 1234.5);): pada locale"C", pemisah desimal adalah titik (.). Format%fmencetak1234.500000. - Baris 8 (
%' flag): modifier'di format specifier meminta grouping ribuan (misal1,234,567.89). Pada locale"C"yang tidak mendefinisikan thousands separator, flag ini diabaikan. - Baris 11 (
setlocale(LC_ALL, "");): argumen""memerintahkan library membaca environment (LANG,LC_ALL) dan memilih locale pengguna. Return value adalah string yang menunjukkan locale aktual—berguna bila kita ingin tahu locale mana yang dipakai. - Baris 16 (
struct lconv *lc = localeconv();): fungsilocaleconvmengembalikan pointer kestruct lconvstatis berisi informasi format lokal. Fielddecimal_pointbiasanya"."di locale C,","di locale Eropa. - Baris 20-21 (
setlocale(LC_COLLATE, "C"); setlocale(LC_TIME, "C");): kita dapat mengubah satu kategori saja tanpa memengaruhi yang lain. Berguna bila kita ingin format angka lokal tetapi collation string tetap ASCII.
LC_COLLATE: Perbandingan String Lokal #
Fungsi strcmp membandingkan string berdasarkan kode byte (ASCII)—selalu sama di semua locale. Fungsi strcoll membandingkan string sesuai aturan collation locale saat ini—di locale Eropa, misalnya, huruf beraksent (é, ü) dapat diurutkan setelah huruf Latin biasa. strcoll menggunakan LC_COLLATE untuk menentukan urutan.
#include <stdio.h>
#include <locale.h>
#include <string.h>
int main(void) {
/* strcoll: bandingkan string sesuai aturan locale */
setlocale(LC_COLLATE, "C");
const char *a = "apple";
const char *b = "Apple";
printf("=== Locale \"C\" ===\n");
printf("strcmp(\"%s\", \"%s\") = %d\n", a, b, strcmp(a, b));
printf("strcoll(\"%s\", \"%s\") = %d\n", a, b, strcoll(a, b));
/* Demonstrasikan perubahan dengan locale yang tersedia */
char *tersedia[] = { "en_US.UTF-8", "C.utf8", "POSIX" };
for (size_t i = 0; i < 3; i++) {
char *r = setlocale(LC_COLLATE, tersedia[i]);
if (r != NULL) {
printf("\n=== Locale %s tersedia ===\n", tersedia[i]);
printf("strcmp(\"%s\", \"%s\") = %d (tidak terpengaruh locale)\n",
a, b, strcmp(a, b));
printf("strcoll(\"%s\", \"%s\") = %d (mengikuti locale)\n",
a, b, strcoll(a, b));
break;
} else {
printf("Locale %s tidak tersedia\n", tersedia[i]);
}
}
/* LC_NUMERIC: format angka */
setlocale(LC_NUMERIC, "C");
printf("\n=== LC_NUMERIC di-\"C\" ===\n");
printf("Pi = %.4f\n", 3.14159);
return 0;
}Output yang Dihasilkan:
=== Locale "C" ===
strcmp("apple", "Apple") = 32
strcoll("apple", "Apple") = 32
Locale en_US.UTF-8 tidak tersedia
=== Locale C.utf8 tersedia ===
strcmp("apple", "Apple") = 32 (tidak terpengaruh locale)
strcoll("apple", "Apple") = 32 (mengikuti locale)
=== LC_NUMERIC di-"C" ===
Pi = 3.1416strcmp("apple", "Apple") = 32 menunjukkan bahwa huruf kecil 'a' (kode 97) lebih besar dari huruf besar 'A' (kode 65) dengan selisih 32. Karena strcoll di locale "C" maupun "C.utf8" sama-sama berbasis ASCII, hasilnya juga 32. Pada sistem dengan locale Eropa terinstal (misalnya de_DE.UTF-8), strcoll dapat mengurutkan "Apple" sebelum "apple" karena mengabaikan case pada pass pertama—berbeda dengan strcmp yang selalu berbasis byte. Loop fallback for mencoba beberapa nama locale secara berurutan; bila satu tidak tersedia, lanjut ke berikutnya. Strategi ini berguna untuk portabilitas antar sistem.
LC_NUMERIC: Format Angka Lokal #
Kategori LC_NUMERIC mengatur bagaimana printf/scanf memformat angka floating-point. Di locale Eropa, pemisah desimal adalah koma ,, bukan titik .. Hal ini penting saat parsing file yang dibuat di sistem berbeda—angka 1.234,56 di locale Eropa berarti 1234.56 di locale Anglo-Saxon.
#include <stdio.h>
#include <locale.h>
int main(void) {
double pi = 3.14159265;
printf("=== LC_NUMERIC = \"C\" ===\n");
setlocale(LC_NUMERIC, "C");
printf("Pi = %.4f\n", pi);
printf("Decimal point: %s\n", localeconv()->decimal_point);
/* Coba locale Eropa (kemungkinan tidak tersedia di sandbox minimal) */
char *r = setlocale(LC_NUMERIC, "de_DE.UTF-8");
if (r == NULL) {
r = setlocale(LC_NUMERIC, "fr_FR.UTF-8");
}
if (r == NULL) {
r = setlocale(LC_NUMERIC, "C.utf8"); /* fallback */
}
printf("\n=== Setelah setlocale(LC_NUMERIC, ...) ===\n");
printf("Locale aktif: %s\n", r);
printf("Decimal point: '%s'\n", localeconv()->decimal_point);
printf("Pi = %.4f\n", pi);
return 0;
}Output yang Dihasilkan:
=== LC_NUMERIC = "C" ===
Pi = 3.1416
Decimal point: .
=== Setelah setlocale(LC_NUMERIC, ...) ===
Locale aktif: C.utf8
Decimal point: '.'
Pi = 3.1416Pada sandbox minimal ini, locale de_DE.UTF-8 dan fr_FR.UTF-8 tidak tersedia—setlocale mengembalikan NULL untuk keduanya. Fallback ke "C.utf8" berhasil, tetapi karena "C.utf8" masih menggunakan titik sebagai pemisah desimal, hasil format Pi tidak berbeda. Pada sistem dengan locale Eropa terinstal, hasilnya akan menjadi Pi = 3,1416 (dengan koma sebagai pemisah desimal). Pola fallback berurutan penting untuk kode yang harus portabel—kita tidak pernah tahu locale mana yang tersedia di mesin pengguna.