İçindekiler
GoUI, github.com/zatrano/goui/i18n içinde küçük, bağımlılığı olmayan bir
çevirmen (translator) sunar. Kasıtlı olarak basittir: locale başına düz JSON
key/value dosyaları, bir fallback locale, ve eksik key'ler için görünür bir
yer tutucu — böylece bozuk bağlantılar (wiring) sessizce boş bir string
olarak render edilmez.
import "github.com/zatrano/goui/i18n"
1. Translator kurulumu
type Translator struct { /* ... */ }
func NewTranslator() *Translator
func (t *Translator) LoadLocale(locale string, filePath string) error
func (t *Translator) Translate(locale, key string, args ...any) string
Uygulama başına bir Translator oluşturun (oturum başına değil, bileşen
başına değil) ve her yerde paylaşın:
tr := i18n.NewTranslator()
if err := tr.LoadLocale("tr", filepath.Join(root, "i18n", "locales", "tr.json")); err != nil {
log.Fatal(err)
}
if err := tr.LoadLocale("en", filepath.Join(root, "i18n", "locales", "en.json")); err != nil {
log.Fatal(err)
}
Ardından bunu ws.NewServer'a verin — bu sunucu tarafından oluşturulan
her oturum aynı *Translator örneğini alır, ve ws.Session her mount
edilen bileşende otomatik olarak SetTranslator'ı çağırır:
server := ws.NewServer(hub, registry, tr)
gouifiber.Register(app, gouifiber.Options{Server: server})
Alt alanları kendiniz inşa ediyorsanız (örn. bir üst form struct'ı içindeki
Tier 1 girdileri), her birinde açıkça SetTranslator'ı çağırın, çünkü
oturum yalnızca üst seviye bileşenin kendi core.BaseComponent'ine
reflection yapar:
c := &ContactForm{ /* ... */ }
c.SetTranslator(tr)
c.Name.SetTranslator(tr)
c.Email.SetTranslator(tr)
Yüklenmiş locale'i olmayan boş bir i18n.NewTranslator() tamamen
geçerlidir — Translate her zaman [[key]] yer tutucusuna geri döner, bu
gerçek metne ihtiyaç duymayan örnekler/demolar için uygundur (bkz.
examples/counter, examples/searchable-select vb., ki bunlar
ws.NewServer(...)'a düz bir i18n.NewTranslator() geçirir).
2. JSON dosya formatı
Locale dosyaları düz JSON nesneleridir: string key'ler string
değerlere eşlenir. LoadLocale açıkça iç içe (nested) JSON nesnelerini
desteklemez — "form.submit" gibi bir key, içindeki nokta yüzünden
sadece namespace'lenmiş görünen tek, düz bir map key'idir,
{"form": {"submit": "..."}} gibi iç içe bir nesne yolu değildir.
{
"welcome_message": "Welcome, {{.Name}}",
"form.required_field": "This field is required",
"form.submit": "Submit",
"form.cancel": "Cancel",
"nav.home": "Home",
"validation.required": "This field is required",
"validation.email": "Please enter a valid email address"
}
Kod tabanının "iç içe-düz key'ler" ("nested-flat keys") ile kastettiği
şey budur: noktalı, hiyerarşik görünen key isimleri
(form.required_field, validation.email,
forms.password_strength.weak), gerçekten iç içe bir JSON ağacı olarak
değil, düz bir map[string]string olarak saklanır. Gerçekten iç içe
bir JSON belgesi ({"form": {"submit": "Submit"}}) yüklemek, LoadLocale'in
beklediği map[string]string'e json.Unmarshal edilirken başarısız olur —
her locale dosyasını bir seviye derinlikte tutun.
Çatının kendi paketlenmiş locale'leri i18n/locales/tr.json ve
i18n/locales/en.json'da yaşar ve Tier 1/Tier 2 kontrolleri tarafından
kutudan çıktığı gibi kullanılan key'leri kapsar (validation.*,
forms.password_strength.*, forms.date_range.invalid,
forms.time_range.invalid, forms.otp.incomplete, vb.). Bunları (veya
kendi kopyalarınızı) yükleyin, böylece bu yerleşik hata mesajları
[[validation.required]] olarak değil çevrilmiş olarak render edilir.
Yer tutucular
Değerler Go text/template yer tutucuları içerebilir. Translate,
eşleşen string'i bir şablon olarak yeniden ayrıştırır ve geçirdiğiniz
ilk variadic argümana karşı yürütür:
tr.Translate("tr", "welcome_message", map[string]any{"Name": "Serhan"})
// → "Hoş geldin, Serhan"
Herhangi bir sebeple interpolasyon başarısız olursa (JSON değerinde kötü
şablon sözdizimi, eşleşmeyen alan adı vb.), Translate hata vermek yerine
ham (interpolasyon yapılmamış) string'i döndürür.
3. Arama ve fallback: önce tr, sonra [[key]]
Translate(locale, key, args...) şu sırayla çözülür:
key'ilocaleiçin yüklenen mesajlarda arayın.- Bulunamazsa (locale hiç yüklenmemiş ya da key ondan eksik), ve
locale != i18n.BaseLocaleise,key'ii18n.BaseLocale("tr"— şu anda Türkçe'ye sabit kodlanmış sabit) için yüklenen mesajlarda arayın. - Hâlâ bulunamazsa,
"[[" + key + "]]"literal string'ini döndürün.
const BaseLocale = "tr"
Bu şu anlama gelir:
- Desteklenmeyen/bilinmeyen bir locale (mesela hiç yüklenmemiş
"de"), o key için"tr"'de her ne varsa şeffaf bir şekilde ona geri döner. - Desteklenen ama sadece bir key'i eksik olan bir locale (örn.
"en"yüklendi ama biri"form.cancel"'ı eklemeyi unuttu) da o tek key için"tr"değerine geri döner, boş bir string'e değil. - Hem istenen locale'de hem de
"tr"'de bulunmayan bir key,"[[the.missing.key]]"olarak render edilir — UI'de görünür, bir demo veya QA sırasında grep'lemesi kolay, ve gerçek metinle karışması imkansız.
tr.Translate("de", "form.submit") // "Gönder" — tr'ye geri döner
tr.Translate("tr", "does.not.exist") // "[[does.not.exist]]"
BaseComponent.T, bunun üzerine bir fallback katmanı daha ekler: bir
bileşene hiç çevirmen enjekte edilmemişse (SetTranslator hiç
çağrılmadıysa), T, arama yapmayı denemeden bile hemen
"[[" + key + "]]" döndürür — böylece eksik bağlantı ve eksik key'ler
aynı ve eşit derecede belirgin görünür.
4. Bir bileşenden çeviri kullanma
func (c *MyComponent) Render() (string, error) {
label := c.T("form.submit") // yer tutucu yok
greeting := c.T("welcome_message", map[string]any{"Name": "Ada"}) // yer tutucuyla
return "<button>" + html.EscapeString(label) + "</button>" +
"<p>" + html.EscapeString(greeting) + "</p>", nil
}
Veya, core.RenderTemplate (otomatik olarak html/template ile escape
eden) üzerinden render ederken, T'yi veride geçirin ve
{{call .T "key" .}}'yi kullanın — tam yer tutucu kuralları için
02-components.md'ye bakın.
core.BaseComponent üzerindeki Locale, T'nin hangi locale'i
kullanacağını seçer; WebSocket URL'sindeki ?locale= sorgu string'i
parametresinden ayarlanır (new GoUIClient('/goui/ws', 'counter', { locale: 'tr' }))
ve ws.Session bir bileşeni mount veya activate ettiğinde yayılır.
Locale boşsa, T, i18n.BaseLocale'i ("tr") kullanır.
5. Yeni bir dil ekleme
-
tr.json/en.jsonile aynı düz key kümesine sahipi18n/locales/<code>.json(veya kendi locale dosyalarınızı tuttuğunuz herhangi bir yer) oluşturun — en azından, yerleşik Tier 1/Tier 2 kontrollerinin dayandığıvalidation.*veforms.*key'leri, artı kendi uygulama key'leriniz. -
Başlangıçta diğerleriyle birlikte yükleyin:
tr := i18n.NewTranslator() _ = tr.LoadLocale("tr", filepath.Join(root, "i18n", "locales", "tr.json")) _ = tr.LoadLocale("en", filepath.Join(root, "i18n", "locales", "en.json")) _ = tr.LoadLocale("fr", filepath.Join(root, "i18n", "locales", "fr.json")) -
İstemcinin
locale: "fr"ile bağlanmasını sağlayın:new GoUIClient('/goui/ws', 'contact', { mount: '#app', locale: 'fr' }).connect(); -
fr.json'da henüz çevirmediğiniz herhangi bir key, sayfayı bozmak yerine sessizcetr(temel locale) değerine geri döner — hangi zaman uygun olursa doldurun, hiçbir build adımı veya kod değişikliği gerekmez.
i18n.BaseLocale'i değiştirmek için şu anda bir çalışma zamanı API'si
yoktur — bu, derleme zamanı bir sabittir ("tr"). Uygulamanızın kanonik
dili Türkçe değilse, birincil/varsayılan locale dosyanızın hâlâ tam
kapsamına sahip olduğundan emin olun, çünkü o, diğer her locale için
fallback hedefidir.