İçindekiler
Unicode ve Karakter Kodlaması: Geliştiriciler için i18n Rehberi
Tek bir kelime çevrilmeden önce, bir yerel ayar değiştirilmeden önce, çoğullama kuralları uygulanmadan önce—metin doğru şekilde depolanmalı, iletilmeli ve işlenmelidir. Bu, karakter kodlamasının alanıdır; ve bunu yanlış yapmak, geliştiricilerin "mojibake" adını verdiği bozuk karakter saçmalığı üretir.
Unicode, dünyanın tüm yazı sistemlerini tek, evrensel bir standartta temsil etme sorununu çözdü. Ancak Unicode'un nasıl çalıştığını ve çeşitli kodlamalarının (UTF-8, UTF-16, UTF-32) yazılım yığınınızla nasıl etkileşime girdiğini anlamak, uluslararası yazılım geliştiren her geliştirici için temel bilgidir.
Unicode Öncesi Dünya: Kodlama Neden Önemlidir
Unicode öncesinde, her ülke ve dilin kendi kodlama standardı vardı. ASCII İngilizceyi (128 karakter) kapsıyordu. Latin-1 (ISO 8859-1) Batı Avrupa karakterlerini ekledi. Windows-1252, Microsoft'un Latin-1 varyantıydı. Shift-JIS Japonca'yı kodluyordu. GB2312 Çince'yi kodluyordu. KOI8-R Rusça Kiril alfabesini kodluyordu.
Sorun şuydu: bu kodlamalar birbiriyle uyumsuzdu. Shift-JIS ile kodlanmış ve Latin-1 olarak görüntülenen bir belge anlamsız karakterler üretiyordu. Windows-1252 ile dizileri depolayan ve UTF-8 ile görüntüleyen bir veritabanı, aksanlı karakterleri bozuyordu. Veriyi kodlama sınırları arasında taşıyan sistemler—özellikle e-posta ve erken web—sürekli mojibake üretiyordu.
Unicode, tüm yazı sistemlerini kapsayan tek bir evrensel kodlama sağlayarak bunu çözmek için tasarlandı.
Unicode: Standart Açıklaması
Unicode bir karakter kümesi standardıdır—her yazı sistemindeki her karaktere benzersiz bir sayı ("kod noktası" olarak adlandırılır) atar. Unicode standardı şunları kapsar:
- Latin yazı sistemleri (İngilizce, Fransızca, Almanca, İspanyolca, vb.)
- Kiril (Rusça, Ukraynaca, Bulgarca, vb.)
- Arapça ve İbranice (RTL yazı sistemleri)
- CJK (Çince, Japonca, Korece) – 90.000'den fazla ideograf
- Devanagari (Hintçe, Sanskritçe)
- Tayca, Tibetçe, Khmer, Birmanca
- Etiyopya (Amharca)
- Matematiksel ve bilimsel semboller
- Emoji (evet, emoji'ler Unicode karakterlerdir)
Toplam Unicode kod uzayı 1.114.112 olası kod noktası içerir (U+0000'dan U+10FFFF'ye kadar); bunların yaklaşık 150.000'i şu anda atanmıştır.
Kod Noktaları ve Karakterler
Bir Unicode kod noktası, U+0000'dan U+10FFFF'ye kadar bir sayıdır. "A" harfi U+0041'dir. "é" harfi U+00E9'dur. 中 Çince karakteri U+4E2D'dir. 🌍 emoji'si U+1F30D'dir.
Ancak kullanıcıların algıladığı anlamda bir "karakter" her zaman tek bir kod noktası değildir. Unicode'un birleştirici karakterleri vardır—önceki karakteri değiştiren ayrı kod noktaları:
- "é" tek bir kod noktası U+00E9 olarak temsil edilebilir (önceden derlenmiş)
- Ya da "e" (U+0065) + birleştirici akut aksanı (U+0301) = é (ayrıştırılmış) olarak
Bu iki temsil görsel olarak özdeştir ancak farklı bayt dizileridir. Bu şunlar için önemlidir:
- Dize karşılaştırması (bu iki dize eşit mi?)
- Dize uzunluğu (kaç karakter?)
- Dize dizinleme (3. konumdaki karakter nedir?)
Unicode normalleştirme bu temsilleri standartlaştırır. NFC (kanonik ayrıştırma, ardından kanonik birleştirme) web kullanımı için en yaygın formdur. NFD her şeyi taban + birleştirici dizilere ayrıştırır.
Vekil Çiftler
Orijinal Unicode tasarımı 65.536 kod noktasını (16 bit) hedefliyordu ve "Temel Çok Dilli Düzlem"i (BMP) kapsıyordu. BMP dışındaki karakterler—birçok CJK ideografı, tarihi yazı sistemleri ve emoji'ler dahil—U+FFFF üzerinde kod noktaları gerektirir.
UTF-16'da BMP dışındaki karakterler, bir karakteri kodlamak için birlikte çalışan iki 16-bitlik birim olan "vekil çiftler" olarak kodlanır. Bu, dahili olarak UTF-16 kullanan JavaScript'te sık karşılaşılan hataların kaynağıdır:
const emoji = '🌍'; // U+1F30D, BMP dışında
// Yanlış: vekil çiftleri ayrı karakterler olarak ele alır
emoji.length; // 2 (1 değil!)
emoji[0]; // '\uD83C' (yüksek vekil, emoji değil)
emoji[1]; // '\uDF0D' (düşük vekil, emoji değil)
// Doğru: Array.from veya dize yineleyicisini kullanın
Array.from(emoji).length; // 1
[...emoji].length; // 1
// Doğru: tam kod noktası için codePointAt
emoji.codePointAt(0); // 127757 (0x1F30D)
// Doğru: for...of, kod birimi değil kod noktasına göre yineler
for (const char of emoji) {
console.log(char); // '🌍'
}
Grafem Kümeleri
Kod noktaları bile her zaman kullanıcıların "karakter" olarak düşündüğü şeyler değildir. Grafem kümesi, tek bir görsel birim olarak işlenen bir dizi kod noktasıdır:
- Taban harf + birleştirici diyakritikler:
ê=e+̂ - Değiştirici ile emoji:
👍🏾(başparmak yukarı + ten rengi değiştirici) = 2 kod noktası, 1 grafem - Aile emojisi:
👨👩👧👦= Sıfır Genişlikli Birleştirici (ZWJ) ile birleştirilmiş 4 kişi emojisi = 1 grafem, 11 kod noktası, 22 UTF-16 kod birimi
Kullanıcının "bir karakter" olarak algılayacağı dize işlemleri için grafem kümelerini kullanmak istersiniz:
// Grafem bölümleme için Intl.Segmenter (modern API)
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
const text = '👨👩👧👦';
const graphemes = [...segmenter.segment(text)];
graphemes.length; // 1 (bir görsel karakter)
UTF-8, UTF-16 ve UTF-32: Kodlamalar
Unicode bir karakter kümesidir. UTF-8, UTF-16 ve UTF-32, Unicode kod noktalarının bayt olarak nasıl temsil edileceğini belirten kodlamalardır.
UTF-8
UTF-8, kod noktası başına 1-4 bayt kullanan değişken uzunluklu bir kodlamadır:
| Kod noktaları | Bayt | Not |
|---|---|---|
| U+0000 – U+007F | 1 bayt | ASCII uyumlu |
| U+0080 – U+07FF | 2 bayt | Genişletilmiş Latin, IPA, İbranice, Arapça |
| U+0800 – U+FFFF | 3 bayt | CJK'nın büyük çoğunluğu |
| U+10000 – U+10FFFF | 4 bayt | Tamamlayıcı (emoji, nadir CJK) |
Avantajlar:
- ASCII uyumlu: ASCII dosyaları geçerli UTF-8'dir
- İngilizce/Latin metin için depolama açısından verimli (karakter başına 1 bayt)
- Kendi kendini senkronize eder: herhangi bir bayt, başlangıç veya devam baytı olarak tanımlanabilir
- Bayt sırası sorunu yoktur
Dezavantajlar:
- Değişken uzunluk, kod noktasına göre O(1) rastgele erişimi imkânsız kılar (doğrusal tarama gerektirir)
- CJK metni karakter başına 3 bayt alır (UTF-16'daki 2 bayta karşılık)
UTF-8 web'deki baskın kodlamadır: HTTP başlıkları, HTML dosyaları, JSON ve çoğu API UTF-8 kullanır. Web yazılımı geliştiriyorsanız UTF-8 varsayılanınızdır.
UTF-16
UTF-16, kod noktası başına 2 veya 4 bayt kullanır:
- BMP karakterleri: 2 bayt
- Tamamlayıcı karakterler: 4 bayt (vekil çiftler)
Kullananlar: Windows API'leri, Java String türü, JavaScript motorları dahili olarak, .NET string türü
Bayt Sırası İşareti (BOM): UTF-16 dosyaları genellikle bayt sırasını (büyük-endian veya küçük-endian) belirtmek için bir BOM (U+FEFF) ile başlar. UTF-16BE ve UTF-16LE iki varyanttır.
UTF-32
UTF-32, kod noktası başına tam olarak 4 bayt kullanır—sabit genişlik. Kod noktasına göre O(1) rastgele erişim gerektiren kod için basittir, ancak ASCII metnine kıyasla 4 kat daha fazla bellek kullanır.
Kullananlar: Python 3 dahili olarak (bazı platformlarda), bazı Unix/Linux API'leri
Yaygın Kodlama Hataları ve Nasıl Düzeltilir
"é" Yerine "é" Görünmesi Hatası
Bu, klasik UTF-8'in Latin-1 olarak yorumlanması durumudur. "é" (U+00E9) nin UTF-8 kodlaması 0xC3 0xA9 iki baytıdır. Latin-1 olarak yorumlandığında: Ã (0xC3) ve © (0xA9).
Düzeltme: Tüm veri ardışık düzeninin tutarlı şekilde UTF-8 kullanmasını sağlayın. Veritabanı bağlantı karakter kümesini (MySQL'de charset=utf8mb4), HTTP yanıt başlıklarını (Content-Type: text/html; charset=UTF-8), dosya okuma kodunuzu ve veri dışa/içe aktarmalarını kontrol edin.
MySQL utf8 ve utf8mb4 Sorunu
MySQL'in utf8 karakter kümesi yalnızca 3 baytlık UTF-8 dizilerini depolar—4 baytlık UTF-8 dizisi gerektiren emoji veya tamamlayıcı CJK karakterlerini depolayamaz.
Düzeltme: Tam Unicode desteği için MySQL'de her zaman utf8mb4 kullanın:
CREATE TABLE content ( body TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ); -- Veya bağlantı düzeyinde ayarlayın: SET NAMES utf8mb4;
Dize Uzunluğu Hataları
# Python 3: len() bayt sayısını veya grafem sayısını değil, kod noktası sayısını döndürür
text = "é" # bir grafem, bir kod noktası
len(text) # 1 ✓
text = "e\u0301" # bir grafem, iki kod noktası (e + birleştirici akut)
len(text) # 2 ✗ (kullanıcı bir karakter görür, Python iki karakter görür)
# Python'da grafem sayısı için grapheme kütüphanesini kullanın:
import grapheme
grapheme.length("e\u0301") # 1 ✓
// JavaScript: length, UTF-16 kod birimi sayısını döndürür
"🌍".length // 2 (emoji 2 UTF-16 birimi alır)
// Kod noktası sayısı için:
[..."🌍"].length // 1 ✓
// Grafem sayısı için:
const s = new Intl.Segmenter();
[...s.segment("🌍")].length // 1 ✓
Harmanlama ve Sıralama
Unicode'da dizeleri sıralamak, bayt değerine göre sıralamakla aynı değildir. "ä" Almanca'da "a" yakınında sıralanmalıdır ancak İsveççe'de "z"den sonra. "ch" geleneksel İspanyolca'da tek bir birim olarak sıralanırdı. Unicode Harmanlama Algoritması (CLDR), yerel ayara özgü sıralama kurallarını tanımlar.
// Yanlış: bayt değerine göre sıralar ['ä', 'z', 'a'].sort() // ['a', 'z', 'ä'] (çoğu yerel ayar için yanlış) // Doğru: yerel ayara duyarlı sıralama ['ä', 'z', 'a'].sort((a, b) => a.localeCompare(b, 'de')); // ['a', 'ä', 'z'] ✓ (Almanca için doğru) ['ä', 'z', 'a'].sort((a, b) => a.localeCompare(b, 'sv')); // ['a', 'z', 'ä'] ✓ (İsveççe için doğru)
Büyük/Küçük Harf Dönüşümü ve Türkçe i
Türkçe'nin noktalı "İ" ve noktasız "ı" harfleri vardır. Türkçe'de küçük "I" harfi "ı"dır ("i" değil) ve büyük "i" harfi "İ"dir ("I" değil). Yerel ayara duyarsız büyük/küçük harf dönüşümü Türkçe dizeleri bozar:
// Yanlış: yerel ayara duyarsız
"Istanbul".toLowerCase() // "istanbul" (İngilizce)
"istanbul".toUpperCase() // "ISTANBUL" (İngilizce)
// Doğru: yerel ayara duyarlı
"Istanbul".toLocaleLowerCase('tr') // "istanbul" (burada aynı)
"istanbul".toLocaleUpperCase('tr') // "İSTANBUL" (noktalı İ)
// Türkçe I hatası:
"I".toLowerCase() // "i" (Türkçe'de yanlış)
"I".toLocaleLowerCase('tr') // "ı" (doğru noktasız ı)
Düzenli İfadeler ve Unicode
JavaScript regex varsayılan olarak kod noktaları üzerinde değil, UTF-16 kod birimleri üzerinde çalışır:
// Yanlış: . bir kod noktası değil, bir UTF-16 kod birimi eşleştirir
/^.$/.test('🌍') // false (emoji 2 kod birimi)
// Doğru: Unicode'a duyarlı regex için u bayrağını kullanın
/^.$/u.test('🌍') // true ✓
// Ayrıca: u bayrağıyla Unicode özellik kaçışları
/\p{Script=Arabic}/u.test('مرحبا') // true ✓
/\p{Emoji}/u.test('🌍') // true ✓
Unicode için Veritabanı Yapılandırması
PostgreSQL
PostgreSQL, tüm modern sürümlerde Unicode'u yerel olarak destekler. Sürümler arasında karakterleri farklı şekilde sayan VARCHAR(n) yerine TEXT sütunlarını (uzunluk sınırı yok) kullanın. Yeni PostgreSQL veritabanları için varsayılan kodlama UTF8 olmalıdır.
MySQL / MariaDB
Yukarıda belirtildiği gibi, her zaman utf8mb4_unicode_ci harmanlamasıyla utf8mb4 kullanın:
ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
SQLite
SQLite, metni varsayılan olarak UTF-8'de depolar. Özel yapılandırma gerekmez.
Redis ve Önbellekler
Redis, dizeleri bayt olarak depolar—kodlama bağımsızdır. İstemci kodunuzun UTF-8'i tutarlı şekilde kodladığından ve çözdüğünden emin olun.
Özet: Unicode i18n Kontrol Listesi
- Tüm dosyalar, veritabanları ve API'ler UTF-8 kodlaması kullanır
- MySQL veritabanları
utf8değilutf8mb4kullanır - HTTP yanıtları Content-Type'da
charset=utf-8bildirir - JavaScript dize işlemleri regex'te
ubayrağı kullanır - Dize uzunluğu hesaplamaları, kullanıcıya görünür durumlarda çoklu kod birimli karakterleri dikkate alır
- Büyük/küçük harf dönüşümü gerektiğinde yerel ayara duyarlı yöntemler kullanır
- Sıralama, uygun yerel ayarla
localeComparekullanır - BOM, beklenmediği durumlarda UTF-8 dosyalarından çıkarılır
- Dize karşılaştırmasından önce Unicode normalleştirme uygulanır
Bu teknik temellerin gerçek yerelleştirme iş akışlarıyla nasıl bağlantı kurduğu hakkında daha fazla bilgi için bkz. yerelleştirme ve uluslararasılaştırma temelleri ve yazılım yerelleştirme.
Uygulamanızı better-i18n ile küreselleştirin
better-i18n, yapay zeka destekli çevirileri, git-native iş akışlarını ve küresel CDN teslimatını tek bir geliştirici odaklı platformda bir araya getirir. Elektronik tablo yönetmeyi bırakın ve her dilde yayın yapmaya başlayın.
Ücretsiz başlayın → · Özellikleri keşfedin · Belgeleri okuyun