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
iharfinin 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.