Ana içeriğe geç

Google Fonts ve Türkçe: latin-ext Neden 85 KB?

Teknik3 dk

Türkçe bir sitede Google Fonts'u açınca ağda iki font dosyası görürsün: latin ve latin-ext. Inter için ikincisi 85 KB, içinden de yalnızca altı karakter lazım.

Google Fonts Fontu Parçalara Böler

Google, her fontu script'lere göre ayrı dosyalara bölüyor: latin, latin-ext, cyrillic, greek, vietnamese. Her parçanın @font-face kuralında bir unicode-range var ve tarayıcı yalnızca sayfada kullanılan karakterlerin parçasını indiriyor.

Türkçe bu bölünmeye tam oturmuyor:

ParçaBoyutTürkçe için gereken
latin48 KBASCII harfler, ç, ö, ü, ı
latin-ext85 KBĞ, ğ, İ, Ş, ş, ₺

Boyutlar Inter variable fontun (v20) woff2 dosyalarından. Türkçe metinde ğ ya da ş neredeyse her sayfada geçtiği için latin-ext de neredeyse her sayfada iniyor. Yani unicode-range burada dosya atlatmıyor ve 85 KB'ın neredeyse tamamı Türkçe'de hiç kullanılmayan karakterler için iniyor.

Next.js: next/font/google

next/font, Google'ın CSS'ini ve font dosyalarını build sırasında indirip kendi sunucundan sunuyor. Tarayıcı Google'a hiç istek atmıyor.

import { Inter } from "next/font/google"
 
const inter = Inter({
  subsets: ["latin", "latin-ext"],
  display: "swap"
})
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="tr" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

subsets seçeneği çoğu kişinin sandığı şeyi yapmıyor. Next.js'in kaynak kodunda bu liste yalnızca hangi dosyaların preload edileceğini belirliyor. Google'ın verdiği tüm parçalar CSS'e yine giriyor. Yani subsets: ["latin"] yazsan da ğ ve ş doğru çıkar, latin-ext sadece preload edilmez.

Seçim şu:

  • ["latin"]: latin-ext CSS okunduktan sonra, geç iner. O ana kadar ğ ve ş içeren kelimelerin bu harfleri fallback fontla çizilir.
  • ["latin", "latin-ext"]: İki dosya da HTML'le birlikte başlar. Preload unicode-range'e bakmadığı için ikisi de her sayfada iner.

Türkçe sitede latin-ext zaten her sayfada lazım olduğu için ikinci seçenek mantıklı.

Birkaç ayar daha:

  • display varsayılanı zaten swap.
  • adjustFontFallback varsayılan olarak true. Next, fontun yerine geçen fallback'i metrikleriyle eşleştirip layout shift'i azaltıyor, elle size-adjust yazmana gerek yok.
  • Variable fontlarda (Inter gibi) weight vermen gerekmiyor. Variable olmayanlarda zorunlu.

Tailwind kullanıyorsan variable seçeneğiyle CSS değişkeni tanımlayabilirsin:

const inter = Inter({
  subsets: ["latin", "latin-ext"],
  variable: "--font-inter"
})
 
// <html lang="tr" className={inter.variable}>
@theme inline {
  --font-sans: var(--font-inter);
}

85 KB'ı Küçültmek

latin-ext dosyasının içinde yüzlerce karakter var, Türkçe için gereken ise yalnızca altısı. Her karakterin bir Unicode kodu var ve unicode-range ile subset bu kodlarla tanımlanıyor:

KodHarf
U+011E, U+011FĞ, ğ
U+0130İ
U+015E, U+015FŞ, ş
U+20BA₺ (Türk lirası)

Dosyayı bu karakterlere indirmek için fonttools kütüphanesinin pyftsubset komutu yeterli. Python'un paket yöneticisi pip ile kurulur: fonttools komutun kendisini, brotli ise woff2 çıktısı için gereken sıkıştırmayı sağlıyor. latin-ext dosyasının adresini Google'ın CSS çıktısında /* latin-ext */ yorumunun altında bulabilirsin. Inter'de 3.276 byte'lık bir dosya çıktı:

pip install fonttools brotli
pyftsubset inter-latin-ext.woff2 \
  --unicodes="U+011E-011F,U+0130,U+015E-015F,U+20BA" \
  --flavor=woff2 \
  --output-file=inter-turkish-wght-normal.woff2

Variable fontta wght ekseni korunuyor. Sonra bu dosyayı kendi @font-face kuralınla, aynı unicode-range ile bağlıyorsun:

@font-face {
	font-family: "Inter Variable";
	font-display: swap;
	font-weight: 100 900;
	src: url("/fonts/inter-turkish-wght-normal.woff2") format("woff2-variations");
	unicode-range: U+011E-011F, U+0130, U+015E-015F, U+20BA;
}

Bunun için fontu kendin barındırman gerekiyor; next/font/google özel subset kabul etmiyor. Bedeli de var: metinde "Łukasz" gibi bir isim geçerse ł harfi hiçbir parçada olmadığı için fallback fontla çıkar.

Sabit bir metin için (logo, başlık) daha basit bir yol var: Google'ın &text= parametresi yalnızca verdiğin harfleri içeren tek bir dosya döndürüyor. text=ğĞşŞİ ile Inter için 4 KB'lık bir dosya geldi. Değişen içerikte bu yol işe yaramaz.

Özet

Türkçe harfler için 85 KB'lık latin-ext dosyasının tamamını indirmen gerekmiyor. Altı karakterlik bir subset aynı işi yaklaşık 3 KB ile görüyor. Özel subset istemiyorsan, Next.js'te latin-ext'i subsets listesine eklemek ve preload'u bilinçli seçmek yeterli.

Kaynaklar