İçeriğe geç
academia.sh

Ders 22 / 22

Uluslararasılaştırma

Metnin koddan ayrılması, çeviri kataloğu ve anahtar tasarımı, çoğul kurallarının dile göre değişmesi, biçimlemenin platforma bırakılması, yön desteği ve yerel ayarın seçilmesi.

İçindekiler

Kimlik akışı tamamlandı: gözlemci tanınıyor, oturumu kendiliğinden sürüyor. Ama tanınan gözlemcinin bir de dili var. Bu kurs boyunca üretilen her metin tek bir dile göre yazıldı — hata iletileri, “oturum düştü” bildirimi, boş sonuç açıklamaları. Sayılar ve tarihler de öyle: ölçüm değeri “−4,2” biçiminde yazıldı, tarih gün-ay-yıl sırasıyla gösterildi.

Kuzey Yamaç istasyonunu başka bir ülkeden izleyen bir gözlemci için bunların hiçbiri doğru değil. Bu ders arayüzü dile ve bölgeye uyarlanabilir kılmanın yapısını kurar.

İki Ayrı İş

Uluslararasılaştırma (internationalization), uygulamayı uyarlanabilir hâle getirmektir: metinlerin koddan ayrılması, biçimlemenin sabitlenmemesi, düzenin yön değişimine dayanması. Bir kez yapılır ve dilden bağımsızdır.

Yerelleştirme (localization), belirli bir dil ve bölge için içeriğin üretilmesidir: çevirilerin yazılması, tarih ve para biriminin o bölgeye uygun seçilmesi. Her dil için ayrı yapılır.

Ayrımın pratik değeri sıradadır. Uluslararasılaştırma yapılmamış bir uygulamaya çeviri eklemek, kodun her yerine dokunmayı gerektirir. Yapılmış bir uygulamaya ikinci dili eklemek yalnız bir katalog dosyası yazmaktır. Bu yüzden tek dilli kalacağı düşünülen uygulamalarda bile metnin koddan ayrılması ucuz bir yatırımdır.

Çeviri Kataloğu ve Anahtar Tasarımı

Çeviri kataloğu, anahtarları iletilere eşleyen bir yapıdır. Kod anahtarı bilir, metni bilmez.

Anahtarın kendisi bir tasarım kararıdır. Anahtar olarak Türkçe cümleyi kullanmak — “2 alan düzeltilmeli” — kolay görünür ama metin her düzeltildiğinde bütün çeviriler kopar. Anlamı tanımlayan yapılandırılmış bir ad — form.ozet — bu bağı korur. Aynı sözcüğün iki yerde farklı çevrilmesi gerekebileceği için anahtar bağlamı da taşır: dugme.kaydet ile menu.kaydet ayrı anahtarlardır, ikisi de Türkçede “Kaydet” olsa bile.

// ceviri-katalogu.mjs — anahtar, yer tutucu ve cogul kurallari
const KATALOG = {
  tr: {
    "form.ozet": { one: "{sayi} alan düzeltilmeli", other: "{sayi} alan düzeltilmeli" },
    "olcum.aralik": "{enAz} ile {enCok} arasında bir değer girin.",
    "oturum.dustu": "Oturumunuz sona erdi.",
  },
  en: {
    "form.ozet": { one: "{sayi} field needs attention", other: "{sayi} fields need attention" },
    "olcum.aralik": "Enter a value between {enAz} and {enCok}.",
  },
};

const VARSAYILAN = "tr";

function ileti(dil, anahtar, degiskenler = {}) {
  const girdi = KATALOG[dil]?.[anahtar] ?? KATALOG[VARSAYILAN]?.[anahtar];
  if (girdi === undefined) return `!${anahtar}`;   // eksik anahtar gizlenmez
  const kalip = typeof girdi === "string"
    ? girdi
    : girdi[new Intl.PluralRules(dil).select(degiskenler.sayi)] ?? girdi.other;
  return kalip.replace(/\{(\w+)\}/g, (tam, ad) =>
    ad in degiskenler ? String(degiskenler[ad]) : tam);
}

console.log("çoğul kategorileri (Intl.PluralRules)");
console.log("sayı".padEnd(6) + ["tr", "en", "ru"].map((d) => d.padEnd(8)).join(""));
for (const sayi of [0, 1, 2, 5, 11, 21, 100]) {
  console.log(String(sayi).padEnd(6) +
    ["tr", "en", "ru"].map((d) => new Intl.PluralRules(d).select(sayi).padEnd(8)).join(""));
}

console.log("\niletiler");
for (const dil of ["tr", "en"]) {
  for (const sayi of [1, 2]) {
    console.log(`  ${dil} sayi=${sayi}: ${ileti(dil, "form.ozet", { sayi })}`);
  }
  console.log(`  ${dil} aralık : ${ileti(dil, "olcum.aralik", { enAz: -60, enCok: 60 })}`);
  console.log(`  ${dil} oturum : ${ileti(dil, "oturum.dustu")}`);
  console.log(`  ${dil} eksik  : ${ileti(dil, "olcum.bilinmeyen")}`);
}
çoğul kategorileri (Intl.PluralRules)
sayı  tr      en      ru      
0     other   other   many    
1     one     one     one     
2     other   other   few     
5     other   other   many    
11    other   other   many    
21    other   other   one     
100   other   other   many    

iletiler
  tr sayi=1: 1 alan düzeltilmeli
  tr sayi=2: 2 alan düzeltilmeli
  tr aralık : -60 ile 60 arasında bir değer girin.
  tr oturum : Oturumunuz sona erdi.
  tr eksik  : !olcum.bilinmeyen
  en sayi=1: 1 field needs attention
  en sayi=2: 2 fields need attention
  en aralık : Enter a value between -60 and 60.
  en oturum : Oturumunuz sona erdi.
  en eksik  : !olcum.bilinmeyen

Çoğul tablosu, elle yazılan bir çoğul kuralının neden yetmediğini gösteriyor. Türkçede ve İngilizcede iki kategori var ve sayi === 1 sınaması ikisinde de doğru sonucu verir. Rusçada dört kategoriden üçü bu tabloda görünüyor ve seçim yalnız sayının kendisine değil son basamağına da bağlı: 21 “one” kategorisine düşerken 11 “many” kategorisinde kalıyor. Kuralı elle yazan kod, üçüncü dil eklendiğinde yeniden yazılır. Intl.PluralRules kuralı dilden okur; katalog yalnız hangi kategoride ne yazılacağını söyler.

Türkçenin iki biçiminin aynı metni taşıması bir tutarsızlık değil. Katalog yapıyı korur; hangi dilin kaç biçim kullandığı o dilin kararıdır. Aynı katalog Rusçaya eklendiğinde few ve many anahtarları da yazılır, kod değişmez.

İki davranış daha çıktıda görünüyor. Eksik anahtar gizlenmiyor: bulunmayan bir anahtar için ünlem işaretli bir gösterim üretiliyor. Boş dizgi döndürmek ya da anahtarı sessizce atlamak, eksik çeviriyi görünmez kılar ve sürüm sonrası fark edilir. Eksik çeviri varsayılan dile düşüyor: İngilizce katalogda bulunmayan oturum iletisi Türkçe geldi. Bu, eksik metnin hiç görünmemesinden iyidir ama bir uyarı işaretidir; geliştirme sırasında kaydedilmelidir.

Son bir kural: iletiler dizgi birleştirmesiyle kurulmaz. “Toplam “ + sayi + “ kayıt” biçiminde yazılan bir metin, sözcük sırası farklı bir dile çevrilemez. Parçalar tek bir kalıpta ve yer tutucuyla durur.

Biçimlemeyi Platforma Bırakmak

Metin dışındaki her şey — sayı, tarih, süre, liste, sıralama — dile ve bölgeye göre değişir ve bu kuralların hepsi platformda hazır bulunur.

// bicimleme.mjs — sayi, tarih, liste ve siralamanin yerel ayara bagliligi
const DILLER = ["tr", "en-US", "de-DE"];
const OLCUM = -4.2;
const AN = new Date("2026-01-14T06:00:00Z");     // sabit an: cikti yeniden uretilebilir

console.log("sıcaklık (birimle)");
for (const d of DILLER) {
  console.log("  " + d.padEnd(6) +
    new Intl.NumberFormat(d, { style: "unit", unit: "celsius",
      minimumFractionDigits: 1 }).format(OLCUM));
}

console.log("büyük sayı");
for (const d of DILLER) {
  console.log("  " + d.padEnd(6) + new Intl.NumberFormat(d).format(1840.5));
}

console.log("tarih ve saat (UTC)");
for (const d of DILLER) {
  console.log("  " + d.padEnd(6) + new Intl.DateTimeFormat(d, {
    dateStyle: "long", timeStyle: "short", timeZone: "UTC" }).format(AN));
}

console.log("göreli zaman (-3 saat)");
for (const d of DILLER) {
  console.log("  " + d.padEnd(6) +
    new Intl.RelativeTimeFormat(d, { numeric: "auto" }).format(-3, "hour"));
}

console.log("liste");
const istasyonlar = ["Kuzey Yamaç", "Sırt", "Vadi"];
for (const d of DILLER) {
  console.log("  " + d.padEnd(6) +
    new Intl.ListFormat(d, { style: "long", type: "conjunction" }).format(istasyonlar));
}

// --- Turkce'ye ozgu iki tuzak ----------------------------------------------
const ADLAR = ["Ordu", "Ödemiş", "Osmaniye", "Sivas", "Şile", "Isparta", "İstanbul"];
console.log("\nsıralama");
console.log("  tr    " + [...ADLAR].sort(new Intl.Collator("tr").compare).join(", "));
console.log("  en    " + [...ADLAR].sort(new Intl.Collator("en").compare).join(", "));
console.log("  ham   " + [...ADLAR].sort().join(", "));

console.log("büyük harfe çevirme");
console.log("  tr    " + "ilk ölçüm".toLocaleUpperCase("tr"));
console.log("  en    " + "ilk ölçüm".toLocaleUpperCase("en"));
sıcaklık (birimle)
  tr    -4,2°C
  en-US -4.2°C
  de-DE -4,2 °C
büyük sayı
  tr    1.840,5
  en-US 1,840.5
  de-DE 1.840,5
tarih ve saat (UTC)
  tr    14 Ocak 2026 06:00
  en-US January 14, 2026 at 6:00 AM
  de-DE 14. Januar 2026 um 06:00
göreli zaman (-3 saat)
  tr    3 saat önce
  en-US 3 hours ago
  de-DE vor 3 Stunden
liste
  tr    Kuzey Yamaç, Sırt ve Vadi
  en-US Kuzey Yamaç, Sırt, and Vadi
  de-DE Kuzey Yamaç, Sırt und Vadi

sıralama
  tr    Isparta, İstanbul, Ordu, Osmaniye, Ödemiş, Sivas, Şile
  en    Isparta, İstanbul, Ödemiş, Ordu, Osmaniye, Şile, Sivas
  ham   Isparta, Ordu, Osmaniye, Sivas, Ödemiş, İstanbul, Şile
büyük harfe çevirme
  tr    İLK ÖLÇÜM
  en    ILK ÖLÇÜM

Bu çıktı ortama bağlıdır: biçimlerin ayrıntısı çalışma zamanının içerdiği yerel ayar verisinin sürümüne göre değişebilir. Yukarıdaki satırlar node --version çıktısı v24.18.0 olan bir kurulumda üretildi. Değişebilen şey biçimin ayrıntısıdır; ilkeler değişmez.

Beş gözlem çıkarılır.

Ondalık ayırıcı ve binlik ayırıcı dile göre yer değiştirir. Türkçe ve Almanca virgülle ondalık yazar, İngilizce noktayla. Sayıyı elle biçimleyen kod bunu kaçırır ve “1.840,5” değerini “1840.5” diye gösterir; daha kötüsü, kullanıcının yazdığı “1,5” değerini yanlış okur. Form doğrulama şemasındaki virgül–nokta dönüşümünün nedeni buydu.

Birimin metne yapışması bile dile bağlıdır. Almanca çıktıda sayı ile derece işareti arasında boşluk var, Türkçede yok.

Tarih sırası ve ay adı değişir; saat biçimi de değişir. İngilizce çıktıda on iki saatlik gösterim, diğerlerinde yirmi dört saatlik. Zaman dilimi ayrıca verilmelidir; örnekte UTC sabitlendiği için çıktı yeniden üretilebilir.

Liste birleştirme dilbilgisidir. Türkçede son ögeden önce “ve”, İngilizcede virgül ve “and”, Almancada “und” gelir. Elle virgül koyup sonuncudan önce bağlaç eklemek her dilde ayrı bir kural demektir.

Sıralama ve harf dönüşümü Türkçede özel durumdur. Ham sıralama kod noktası sırasını verir ve Türkçe alfabeye hiç uymaz. Türkçe sıralamada O ile Ö, S ile Ş ayrı harflerdir ve bu sırayla dizilir; İngilizce sıralamada aynı harfin çeşitleri sayılırlar, bu yüzden “Ödemiş” iki listede farklı yerde durur. Harf dönüşümünde de i harfinin büyüğü Türkçede İ, diğer dillerde I’dır; yerel ayar verilmeden yapılan bir dönüşüm, çalıştığı makinenin ayarına göre değişen sonuç üretir.

Yön Desteği

Sağdan sola yazılan diller yalnız metnin değil düzenin de yön değiştirmesini gerektirir. İş üç düzeyde ayrılır.

Belge düzeyi. Kök ögede yön bildirilir; yön bilgisi bir dil özelliği değil bir yazım sistemi özelliğidir, bu yüzden dil bildiriminden ayrı verilir. Yön değiştiğinde metnin hizası, listelerin madde işareti tarafı ve kaydırma yönü kendiliğinden döner.

Düzen düzeyi. CSS ile Görsel Sunum kursunda tanıtılan mantıksal kenar adları burada karşılığını bulur: sol ve sağ yerine blok ve satır içi eksenin başlangıç ve bitiş kenarları yazıldığında düzen yön değişimine kendiliğinden uyum sağlar. Fiziksel yönle yazılmış her kural, yön desteği eklenirken tek tek gözden geçirilmelidir.

Metin düzeyi. Karışık yönlü metin — sağdan sola bir cümlenin içinde soldan sağa bir istasyon kodu — özel bir sorun çıkarır: sıralama algoritması sınırdaki noktalama işaretlerini yanlış tarafa taşıyabilir. Kullanıcıdan gelen ya da veriden gelen bir parçanın çevresindeki metinden yalıtılması gerekir; işaretlemede bunun için ayrılmış bir öge vardır ve bu ögenin varsayılan yön kestirimi parçanın kendi içeriğine bakar.

Simgelerin bir kısmı da yönlüdür: ileri oku, geri oku, girinti işaretleri yön değişiminde aynalanır. Saat yönü, ses düzeyi gibi yönü olmayan simgeler aynalanmaz.

Yerel Ayarın Seçilmesi ve Kataloğun Yüklenmesi

Hangi dilin kullanılacağı üç kaynaktan gelir ve öncelik sırası bellidir. Kullanıcının açık tercihi en üsttedir ve saklanır. Yoksa adresin kendisi — dil kodunu taşıyan bir yol parçası ya da alt alan adı — kullanılır; bu, bağlantının paylaşılabilir olmasını sağlar. İkisi de yoksa tarayıcının bildirdiği dil listesi, yani Accept-Language başlığı değerlendirilir.

Tarayıcının listesi bir sıralı tercihtir, tek bir dil değildir. Eşleme yaparken tam eşleşme aranır, bulunamazsa bölge düşürülerek denenir; hiçbiri tutmazsa varsayılan dile düşülür. Yerel ayar bir dilden fazlasını içerir — bölge, takvim, sayı sistemi — bu yüzden metin dili ile biçimleme ayarının farklı olabileceği durumlar vardır.

Katalog yüklemesi bir paketleme kararıdır. Bütün dilleri tek pakette göndermek, kullanıcıya kullanmayacağı metinleri indirtir. Dil başına ayrı bir parça üretip yalnız seçilen dili yüklemek doğru yoldur; bu, Oluşturma Stratejileri kursundaki kod bölme konusuyla doğrudan ilişkilidir. Dil değiştiğinde yeni katalog yüklenene kadar geçen sürede arayüzün ne göstereceği — eski dil mi, boşluk mu — Yükleme ve Hata Durumları dersindeki durum makinesine bağlanır.

Son olarak, çeviri metinleri yer kaplar. Aynı cümle bir dilde diğerinden belirgin biçimde uzun olabilir; sabit genişlikli düğmeler ve tek satıra sığdırılmış etiketler çeviriyle taşar. Düzenin metin uzunluğuna dayanması bir uluslararasılaştırma gereğidir.

Özet

  • Uluslararasılaştırma uygulamayı uyarlanabilir kılar ve bir kez yapılır; yerelleştirme her dil için ayrı üretilen içeriktir.
  • Çeviri kataloğu anlamı tanımlayan yapılandırılmış anahtarlar kullanır; kaynak cümleyi anahtar yapmak, metin düzeltildiğinde bütün çevirileri koparır.
  • Çoğul kategorileri dile göre değişir ve sayının son basamağına bağlı olabilir; kural platformdan okunur, katalog yalnız kategori karşılıklarını taşır.
  • İletiler dizgi birleştirmesiyle değil tek bir kalıp ve yer tutucularla kurulur; eksik anahtar gizlenmez, eksik çeviri varsayılan dile düşer.
  • Sayı, tarih, göreli zaman, liste ve sıralama platformun yerel ayar araçlarına bırakılır; Türkçede sıralama ve i harfinin dönüşümü yerel ayar verilmeden doğru sonuç vermez.
  • Yön desteği belge, düzen ve metin düzeylerinde ayrı ele alınır; mantıksal özellikler düzeni, yalıtım ögesi karışık yönlü metni çözer.

Kurs Kapanışı

Bu kurs, bir uygulamanın iç mimarisini dört soruyla kurdu.

Kullanıcı nerede? Yönlendirme konusu adresi uygulamanın durumunun bir parçası hâline getirdi: iç içe düzenler ortak kabuğu paylaştırdı, rota parametreleri durumu adreste taşıdı, rota koruma erişimi görünürlük düzeyinde yönetti, kod bölme her rotanın bedelini kendine ödetti.

Bilgi nerede duruyor? Durum yönetimi konusu durumu türlerine ayırdı — yerel, paylaşılan, sunucu ve adres durumu — ve her biri için farklı bir kap gösterdi. Sunucu durumunun ayrı ele alınması, sonraki konunun kapısını açtı.

Bilgi nasıl geliyor? Veri erişimi konusu istek katmanını ve hata sözleşmesini kurdu, sorgu tabanlı erişimin önbellek normalizasyonunu gösterdi, akış hâlinde gelen veriyi kopmaya dayanıklı biçimde tüketti, görünümün durumlarını sayılı bir kümeye indirdi ve ağ kararsızlığını kullanıcıya yansımadan soğurdu.

Bilgi nasıl giriliyor ve kim giriyor? Formlar ve kimlik konusu değerin nerede tutulacağını, kuralların iki tarafta nasıl aynı kalacağını, hatanın nasıl algılanabilir olacağını, kimlik kanıtının nerede saklanacağını, oturumun nasıl süreceğini ve arayüzün dile nasıl uyarlanacağını kurdu.

Dördünün ortak yanı, hepsinin tarayıcının içinde geçmesi. Uygulama açıldıktan sonrasını anlattık; açılmadan önce ne olduğunu hiç sormadık. Oysa aynı uygulama üç ayrı yerde oluşturulabilir: kullanıcının tarayıcısında, isteği karşılayan sunucuda ya da daha istek gelmeden derleme sırasında. Seçim, ilk ekranın ne kadar sürede geldiğini, arama motorunun ne gördüğünü, sunucunun ne kadar iş yaptığını ve içeriğin ne kadar taze olabileceğini belirler.

Bir sonraki kurs — Oluşturma Stratejileri ve Altyapı — bu seçimi ödünleşimleriyle kurar: istemcide, sunucuda ve derleme zamanında oluşturmanın maliyetleri, statik çıktının seçici tazelenmesi, etkileşimin parça parça kazanılması, paketleme ve varlık iyileştirme, ve uygulamanın kullanıcıya ulaştığı dağıtım hattı.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat