diff --git a/docs/self-hosted-redesign/neta-multilingual-i18n-master-plan.md b/docs/self-hosted-redesign/neta-multilingual-i18n-master-plan.md index 5c0e1eb..35f4362 100644 --- a/docs/self-hosted-redesign/neta-multilingual-i18n-master-plan.md +++ b/docs/self-hosted-redesign/neta-multilingual-i18n-master-plan.md @@ -1,1337 +1,1204 @@ --- -title: Neta Çok Dilli Sistem ve Yerelleştirme Ana Planı -description: Self-host edilen Neta instance'larında yönetilebilir arayüz dilleri, çok dilli içerik formları, müşteri portalı dili ve mobil API uyumu için faz bazlı uygulama planı. -status: completed -current_phase: "completed" -last_updated: 2026-07-19 +title: Neta Çok Dilli Sistem V2 Ana Planı +description: Ayarlar bilgi mimarisi, owner ve müşteri dil tercihleri, yönetilebilir arayüz çevirileri ve tüm dinamik içerik formları için sayfa bazlı uygulama planı. +status: planned +current_phase: "faz-0" +last_updated: 2026-07-20 +supersedes: "neta-multilingual-i18n-v1-legacy-plan.md" --- -# Neta Çok Dilli Sistem ve Yerelleştirme Ana Planı +# Neta Çok Dilli Sistem V2 Ana Planı -## 1. Belgenin amacı +## 1. Neden yeni bir plan gerekiyor? -Bu plan, Neta'yı yalnızca Türkçe metinler gösteren bir uygulamadan, self-host eden -kişinin yönetebildiği çok dilli bir platforma dönüştürür. +İlk i18n uygulaması veri modeli ve temel runtime açısından kullanılabilir parçalar +üretmiş olsa da ürün davranışı ve uygulama kapsamı hedeflenen yapıdan sapmıştır. +Bu belge mevcut uygulamayı bitmiş kabul etmez; onu denetlenmesi ve düzeltilmesi +gereken bir altyapı olarak ele alır. -Hedef sistemde: +Kod incelemesinde doğrulanan temel problemler: -- Türkçe (`tr`) ve İngilizce (`en`) ilk kurulumda hazır ve aktif gelir. -- Instance sahibi Ayarlar sayfasından yeni bir dil ekleyebilir. -- Eklenen dil için sidebar, sayfa başlıkları, butonlar, durumlar, formlar, - doğrulama mesajları, auth ekranları ve müşteri portalı metinleri çevrilebilir. -- Çevrilebilir iş içerikleri, aynı form içinde dil sekmeleriyle girilebilir. -- Müşteri portal daveti oluşturulurken müşterinin dili seçilir. -- Davet ekranı dahil olmak üzere müşteri portalı seçilen dilde açılır. -- Tarih, saat, sayı ve para gösterimleri seçili locale'e göre biçimlendirilir. -- Web ile aynı dil ve içerik modeli gelecekteki React Native istemcisi tarafından - API üzerinden kullanılabilir. -- Yeni bir dil eklemek yeni bir deployment veya kod değişikliği gerektirmez. +- Login, register ve reset-password ekranlarında dil seçici bulunuyor. +- Auth öncesi locale çözümlemesi cookie'yi dikkate aldığı için public auth + ekranının dili kullanıcı tarafından değiştirilebiliyor. +- `/settings` yaklaşık 1.200 satırlık tek bir client component ve görünüm, marka, + profil, güvenlik, AI, kişisel dil tercihi, dil yönetimi ve çeviri editörünü aynı + ekranda topluyor. +- Settings menüsü component içi state ile tab değiştiriyor; her bölümün ayrı URL, + loading/error sınırı ve server-side authorization noktası yok. +- Portal shell `/portal/settings` bağlantısı üretiyor fakat bu route mevcut değil. +- Türkçe ve İngilizce katalogların büyük kısmı yalnız sayfa başlığı ve birkaç + aksiyondan oluşuyor; ekranların içindeki form, dialog, toast, tablo, filtre, + boş durum ve validation metinlerinin çoğu hâlâ hard-coded. +- Türkçe karakter içeren sabit UI metinleri dashboard, analiz, takvim, müşteri, + proje, görev, finans, günlük, sohbet, business ve ortak component dosyalarında + hâlâ bulunuyor. +- Mevcut içerik translation registry yalnızca branding, project, + planning_section, task, calendar_event ve client alanlarının küçük bir kısmını + tanımlıyor. +- Finans, günlük, müşteri aktiviteleri, teklifler, sözleşmeler ve abonelikler gibi + kullanıcı tarafından üretilen metinler kapsam dışında kalmış. +- `LocalizedFields` bileşeninin kendi label, badge ve hata metinleri Türkçe sabit. +- Dashboard layout tüm namespace'leri her sayfaya gönderiyor; sayfa bazlı katalog + yükleme sınırı henüz uygulanmıyor. -Bu belge implementasyon sırasını, veri modelini, fallback davranışını, UI -sözleşmelerini, migrasyon stratejisini, güvenlik sınırlarını ve test kapılarını -tanımlar. +Önceki plan ve faz belgeleri geçmiş uygulamanın kaydı olarak +[`neta-multilingual-i18n-v1-legacy-plan.md`](neta-multilingual-i18n-v1-legacy-plan.md) +dosyasına taşınmıştır. Bu dosyadaki checkbox'lar yalnız V2 çalışmasının durumunu +gösterecektir. -## 2. Mevcut sistem inceleme özeti +## 2. Ürün hedefi -Codebase incelemesinde çok dillilik açısından aşağıdaki mevcut durum tespit edildi: +Neta'yı self-host eden kişi: -- `user_preferences.language` alanı bulunuyor ve varsayılanı `tr`. -- Aynı alanda SQLite check constraint yalnızca `tr` ve `en` değerlerine izin - veriyor. -- Preference service şu anda yalnızca `colorMode` döndürüyor; dil tercihini - runtime'a taşımıyor. -- Root layout içindeki `` sabit. -- Sidebar konfigürasyonları Türkçe başlıkları doğrudan taşıyor. -- Dashboard, ayarlar, auth ve portal ekranlarında kullanıcıya gösterilen metinler - bileşenlerin içine gömülü. -- Tarih ve para gösterimlerinde `tr-TR` ve `date-fns/locale/tr` doğrudan - kullanılıyor. -- Müşteri daveti oluşturulurken locale seçilmiyor veya davete kaydedilmiyor. -- `clients`, `app_profiles` ve `portal_invitations` üzerinde portal dili alanı yok. -- Proje, görev ve planlama alanlarının başlık/açıklamaları tek sütunda tutuluyor. -- Mobil discovery ve `/api/v1/meta` sözleşmesinde desteklenen diller ilan - edilmiyor. -- Arayüz metni içerme ihtimali bulunan yaklaşık 141 TypeScript/TSX dosyası var. +- Sistemin varsayılan dilini Türkçe veya İngilizce yapabilmeli. +- Kendi hesabında aktif dillerden birini kişisel arayüz dili olarak seçebilmeli. +- Yeni bir dil ekleyebilmeli, bu dilin bütün sistem metinlerini çevirebilmeli ve + yeterli tamamlanma seviyesine ulaştığında aktif edebilmeli. +- Arayüz çevirilerini kod değiştirmeden düzenleyebilmeli, içe/dışa + aktarabilmeli. +- Proje, görev, finans işlemi ve diğer çevrilebilir kayıtları oluştururken aktif + dil sayısı kadar dil tab'ı görebilmeli. +- Her dil tab'ında yalnız metinsel ve gerçekten çevrilebilir alanları + doldurabilmeli. +- Bir müşteriye portal hesabı açarken müşterinin başlangıç dilini seçebilmeli. -Bu nedenle yalnızca bir çeviri kütüphanesi eklemek yeterli değildir. Arayüz -yerelleştirmesi, kullanıcı içeriği çevirileri, portal locale çözümleme ve API -sözleşmesi birlikte ele alınmalıdır. +Portal kullanıcısı: -## 3. Temel kavramlar +- İlk kez admin tarafından atanan portal dilini görmeli. +- Hesabı açıldıktan sonra `/portal/settings/language` üzerinden yalnız admin + tarafından aktif edilmiş diller arasında geçiş yapabilmeli. +- Kendi seçimi olmadığı sürece admin tarafından atanan portal varsayılanını + kullanmalı. +- Proje, görev, plan ve diğer paylaşılan içerikleri kendi dilinde; çeviri yoksa + açıkça tanımlanmış fallback zinciriyle görmeli. -Bu projede iki farklı veri türü birbirinden kesin olarak ayrılacaktır. +## 3. Kesin ürün kararları -### 3.1. Arayüz çevirisi +### 3.1. Auth ekranlarında dil davranışı -Uygulamanın kendisine ait metinlerdir: +- Login ekranında dil seçici olmayacak. +- İlk admin kayıt ekranında dil seçici olmayacak. +- Şifre unuttum ve şifre sıfırla ekranlarında dil seçici olmayacak. +- Standart public auth ekranları instance varsayılan diliyle açılacak. +- Browser `Accept-Language` veya eski `neta_locale` cookie'si public login dilini + değiştirmeyecek. +- Portal davet ekranı bir istisnadır: adminin davette belirlediği snapshot locale + ile açılır fakat burada da dil seçici bulunmaz. +- Başarılı login sonrasında authenticated kullanıcının kendi tercihi çözülür. -- Sidebar grup ve menü adları -- Sayfa başlıkları -- Buton, tab ve dialog metinleri -- Form label ve placeholder'ları -- Boş durum ve onay mesajları -- Toast ve kullanıcıya gösterilen hata metinleri -- Durum, öncelik ve kategori label'ları -- Login, kayıt ve davet ekranları -- Müşteri portalındaki sistem metinleri - -Bu metinler stabil anahtarlarla çağrılır: - -```ts -t("navigation.projects") -t("projects.form.name.label") -t("status.project.completed") -``` - -Türkçe ve İngilizce kaynak katalogları kod içinde sürümlenir. Instance sahibinin -yaptığı değişiklikler ve sonradan eklenen diller SQLite içinde saklanır. - -### 3.2. İçerik çevirisi - -Freelancer'ın oluşturduğu ve müşteriye gösterilebilecek iş verileridir: - -- Proje adı ve açıklaması -- Proje kapak görseli alt metni -- Proje planlama bölümü başlığı ve içeriği -- Müşteriye açık görev başlığı ve açıklaması -- Portal karşılama ve footer metinleri -- İleride teklif, sözleşme ve benzeri portal içerikleri - -Bu içerikler formdaki locale tab'larıyla girilir ve `content_translations` -tablosunda saklanır. - -### 3.3. Çevrilmeyecek alanlar - -Her form alanını dil başına çoğaltmak doğru değildir. Aşağıdaki alanlar ortak -kalır: - -- Tarih ve saat -- Tutar, para birimi ve vergi -- Durum, öncelik ve kategori enum değerleri -- Müşteri/proje ilişkileri -- İlerleme yüzdesi -- E-posta, telefon ve URL -- Dosya ve görselin kendisi -- Teknik ID'ler - -Bu alanların değerleri ortak tutulur; kullanıcıya gösterilen label'ları arayüz -sözlüğünden çevrilir. - -### 3.4. Yazıldığı dilde kalacak içerikler - -İletişim niteliğindeki kayıtlar otomatik olarak çoğaltılmayacaktır: - -- Müşterinin yazdığı revizyon talebi -- Chat mesajları -- Freelancer'ın özel günlüğü -- Müşteri aktivite notları - -Bu içeriklerde gerekirse `sourceLocale` metadata'sı saklanabilir; metnin kendisi -yazıldığı dilde gösterilir. Otomatik çeviri ayrı ve opsiyonel bir ürün özelliğidir. - -## 4. Kilitlenecek ürün kararları - -Faz 0 tamamlanırken aşağıdaki kararlar ADR ile kesinleştirilecektir. Bu planın -önerilen varsayımları şöyledir: - -- [x] Türkçe ve İngilizce silinemez built-in diller olacak. -- [x] İlk migration sonrasında instance varsayılan dili Türkçe olacak. -- [x] Yeni eklenen dil önce `draft`, ardından `active` durumuna alınacak. -- [x] Eksik çeviri halinde sayfada çeviri anahtarı değil fallback metni gösterilecek. -- [x] Yeni locale için instance sahibi `tr` veya `en` fallback dili seçecek. -- [x] Portal için kritik çevirileri tamamlanmayan dil müşteri dili olarak - yayınlanamayacak. -- [x] Dashboard ve portal route'larına `/tr`, `/en` gibi URL prefix'i eklenmeyecek. -- [x] Locale; kullanıcı tercihi, davet kaydı ve güvenli cookie üzerinden - çözümlenecek. -- [x] Arayüz çeviri sistemi için ağır bir runtime bağımlılığı eklenmeyecek; mevcut - `Intl`, React ve SQLite temelli küçük bir Neta i18n katmanı yazılacak. -- [x] İlk sürümde makine çevirisi zorunlu olmayacak. -- [x] Çeviri paketi JSON olarak içe ve dışa aktarılabilecek. -- [x] İçerik formlarında yalnızca çevrilebilir metin alanları locale tab'ları - içinde olacak. -- [x] Instance varsayılan dilindeki zorunlu alanlar boş bırakılamayacak. -- [x] Built-in Türkçe ve İngilizce katalogları release sırasında yüzde 100 - tamamlanmış olmak zorunda olacak. - -## 5. Hedef mimari +### 3.2. Owner/freelancer locale önceliği ```text -Kod ile gelen anahtar kataloğu - ├── tr katalog - └── en katalog - | - v -SQLite locale kayıtları + instance çeviri override'ları - | - v -Locale resolver - ├── Auth öncesi: davet / cookie / instance default - ├── Freelancer: user preference / instance default - └── Client: user preference / client portal locale / instance default - | - v -Translator - ├── Server Components - ├── Server Actions / hata kodları - ├── Client Components provider - └── API locale context - | - +--------------------------+ - | | - v v -Arayüz sözlüğü İçerik translation resolver -sidebar, label, hata project/task/planning/branding +user_preferences.language + -> instance_i18n_settings.default_locale + -> built-in tr ``` -### 5.1. Önerilen klasör yapısı +- Owner dilini `/settings/language` sayfasından değiştirir. +- Seçim yalnız `active` diller arasından yapılır. +- Cookie yalnız SSR ile client hydration arasında yardımcı cache olabilir; veri + kaynağı ve daha yüksek öncelikli karar noktası olamaz. + +### 3.3. Portal locale önceliği ```text -lib/i18n/ - types.ts - keys.ts - catalog.ts - format.ts +portal kullanıcısının user_preferences.language + -> clients.portal_locale (adminin belirlediği başlangıç dili) + -> instance default locale + -> built-in tr +``` -locales/ - tr/ - common.ts - auth.ts - navigation.ts - dashboard.ts - clients.ts - projects.ts - tasks.ts - calendar.ts - finance.ts - journal.ts - chat.ts - settings.ts - portal.ts - validation.ts - en/ - ... +- Davet oluşturulurken `clients.portal_locale` ve + `portal_invitations.locale` aynı seçimi taşır. +- Davet locale'i davet ekranı için immutable snapshot'tır. +- Davet kabul edildiğinde müşteri preference kaydı başlangıçta bu locale ile + oluşturulur. +- Müşteri daha sonra kendi preference değerini değiştirebilir. +- Adminin client varsayılanını daha sonra değiştirmesi, müşterinin açık kişisel + tercihini sessizce ezmez. +- Bir dil arşivlenirse o dili kullanan tercihler kontrollü fallback'e düşer ve + ayarlar ekranında yeniden seçim istenir. -server/i18n/ - locale.ts - resolver.ts - translator.ts - catalog.ts - service.ts - content.ts +### 3.4. Instance varsayılan dilinin rolü + +Instance varsayılanı: + +- Public auth ve sistem ekranlarının dilidir. +- Yeni owner/freelancer preference kaydının başlangıç değeridir. +- Yeni müşteri davetinde ön-seçili portal dilidir. +- Eksik veya geçersiz kullanıcı tercihlerinin fallback'idir. +- Çevrilebilir içerik formlarında ilk ve zorunlu tab'dır. + +Instance varsayılanı, mevcut kullanıcıların açık kişisel tercihlerini topluca +değiştirmez. + +### 3.5. Built-in ve custom diller + +- Türkçe (`tr`) ve İngilizce (`en`) built-in gelir. +- Built-in diller silinemez. +- Self-host eden kişi built-in metinleri override edebilir. +- Yeni dil `draft` oluşturulur, fallback dili seçilir ve çevrilir. +- Yeterli katalog bütünlüğü olmadan `active` yapılamaz. +- Yalnız `active` diller kullanıcı tercihlerinde, müşteri davetinde ve içerik + form tab'larında gösterilir. +- `draft` dil çeviri editöründe görünür fakat son kullanıcıya sunulmaz. +- Kullanımda olan veya default olan dil arşivlenmeden önce bağımlılıklar çözülür. + +## 4. Ayarlar bilgi mimarisi + +Tek `/settings` client component kaldırılacak. Ayarlar, dashboard ana +sidebar'ındaki tek `Ayarlar` menüsünden açılan ve kendi kalıcı iç navigasyonuna +sahip route grubu olacaktır. + +```text +/settings + /general + /appearance + /profile + /security + /ai + /language + /languages + /languages/new + /languages/[locale] + /languages/[locale]/translations + /languages/import-export + +/portal/settings + /language + /appearance + /profile + /security +``` + +### 4.1. Desktop yerleşimi + +```text +┌──────────────────────────────────────────────────────────────┐ +│ Ayarlar │ +├──────────────────┬───────────────────────────────────────────┤ +│ Genel │ Aktif alt sayfanın başlığı │ +│ Görünüm │ │ +│ Profil │ Route'a özel form / liste / editor │ +│ Güvenlik │ │ +│ Yapay zekâ │ │ +│ Dil tercihi │ │ +│ Diller │ │ +└──────────────────┴───────────────────────────────────────────┘ +``` + +- İç sidebar settings layout içinde sticky kalır. +- Aktif öğe pathname üzerinden belirlenir. +- İç sidebar tüm ayar sayfalarında aynı konumda kalır. +- Her alt sayfa kendi Server Component veri yüklemesine ve action dosyasına + sahip olur. +- Form submit sırasında tüm ayarlar verisi tekrar yüklenmez. +- Owner-only ve user-level sayfalar navigasyonda görsel olarak ayrılır. + +### 4.2. Mobil yerleşim + +- İç sidebar üstte dropdown veya yatay scroll olan kompakt settings nav'a döner. +- Sayfa değişimi gerçek route navigasyonudur. +- Form aksiyonları sticky footer kullanabilir; viewport yatay scroll yapmaz. +- Translation editor küçük ekranda kaynak ve hedef alanları alt alta gösterir. + +### 4.3. Settings yetki matrisi + +| Sayfa | Owner/freelancer | Portal client | +| --- | --- | --- | +| Genel/branding | Yönetir | Göremez | +| Görünüm | Kişisel tema + owner marka | Kişisel tema | +| Profil | Kendi hesabı | Kendi hesabı | +| Güvenlik | Kendi hesabı | Kendi hesabı | +| AI | Yönetir | Göremez | +| Dil tercihi | Aktif dillerden seçer | Aktif dillerden seçer | +| Diller ve çeviriler | Yönetir | Göremez | + +## 5. Dil yönetimi UX'i + +### 5.1. `/settings/language` — kişisel dil tercihi + +Bu sayfa yalnız oturumdaki owner/freelancer'ın uygulama dilini değiştirir. + +- Aktif diller kart/radio listesi olarak gösterilir. +- Native name, yönetim adı ve locale kodu görünür. +- Seçim kaydedilince preference DB'ye yazılır ve layout yenilenir. +- “Instance varsayılanı” ayrı bir bilgi satırında gösterilir. +- Bu ekran custom dil oluşturmaz veya katalog düzenlemez. + +### 5.2. `/settings/languages` — dil yönetimi + +- Üstte instance varsayılan dil seçimi bulunur. +- Dil listesi tablo/kart halinde ayrı satırlardır. +- Her satırda native ad, kod, durum, fallback, UI tamamlanma oranı, çevrilebilir + içerik durumu ve kullanım sayısı görünür. +- Birincil aksiyon `Dil ekle`, satır aksiyonu `Yönet` olur. +- Draft/active/archived durumları yalnız renk ile anlatılmaz. +- Built-in, default ve kullanımda rozetleri ayrı gösterilir. +- Tehlikeli lifecycle aksiyonları confirmation dialog kullanır. + +### 5.3. `/settings/languages/new` — dil ekleme + +- Locale kodu BCP 47 olarak doğrulanır ve canonical hale getirilir. +- Yönetim adı, native ad, yazı yönü ve fallback dili alınır. +- Dil her zaman `draft` oluşturulur. +- Başarılı işlem `[locale]` detayına yönlendirir. +- Aynı kod, self fallback, fallback loop ve geçersiz yön engellenir. + +### 5.4. `/settings/languages/[locale]` — dil detayı + +- Dil metadata'sı ve lifecycle burada yönetilir. +- Tamamlanma özeti namespace ve sayfa bazında gösterilir. +- Eksik kritik alanlar doğrudan ilgili translation editor filtresine bağlanır. +- Aktifleştirme öncesi readiness checklist görünür. +- Default yapma, aktif etme, arşivleme gibi aksiyonlar etkilerini açıklar. +- Kullanıcı ve client kullanım sayıları gösterilir. + +### 5.5. `/settings/languages/[locale]/translations` — çeviri editörü + +- Kullanıcı teknik key kalabalığına bırakılmaz; önce `Sayfa/Modül`, sonra bölüm + seçer. +- Arama; kaynak metin, hedef metin ve teknik key üzerinde çalışır. +- Türkçe ve İngilizce referans metinleri erişilebilir şekilde gösterilir. +- Hedef dil input'u, fallback preview ve override durumu ayrıdır. +- `Tümü`, `Eksikler`, `Değiştirilenler`, `Portal için gerekli` filtreleri vardır. +- Satır bazlı otomatik kaydetme veya açık toplu kaydetme davranışından yalnız biri + seçilip tutarlı uygulanır; önerilen model dirty-state + toplu kaydetmedir. +- Sayfadan kaydedilmemiş değişiklikle çıkış uyarısı bulunur. +- Reset, built-in/fallback metnine döner ve sonucu preview eder. +- Tamamlanma yüzdesi yalnız key varlığını değil boş değer ve interpolation + uyumunu da denetler. +- Import/export ayrı route'a taşınır; ana editörü kalabalıklaştırmaz. + +## 6. Arayüz çevirisi sözleşmesi + +Her fazda bir sayfa tam olarak bitmeden sonraki sayfaya geçilmez. Bir sayfanın +“çevrildi” sayılması için yalnız başlık yeterli değildir. + +Her sayfada aşağıdaki yüzeyler denetlenir: + +- Page title ve metadata +- Header aksiyonları +- Stats kartları +- Tab, filtre, sort ve arama alanları +- Form label, placeholder ve helper metinleri +- Select seçenekleri ve enum label'ları +- Tablo kolonları ve pagination +- Kart/list/kanban görünümleri +- Dialog, sheet, popover ve tooltip +- Empty, loading, error ve permission state'leri +- Confirmation mesajları +- Toast mesajları +- Server action ve API'den kullanıcıya gösterilen hatalar +- `aria-label`, image alt ve screen reader metinleri +- Tarih, saat, sayı, yüzde ve para formatları + +### 6.1. Namespace yapısı + +Kataloglar gerçek dosyalara ayrılacaktır; yalnız `tr/index.ts` ve `en/index.ts` +içinde dev objeler tutulmayacaktır. + +```text +locales/{locale}/ + common.ts + auth.ts + navigation.ts + dashboard.ts + analytics.ts + calendar.ts + clients.ts + client-detail.ts + projects.ts + project-detail.ts + tasks.ts + finance.ts + journal.ts + chat.ts + proposals.ts + invoices.ts + subscriptions.ts + settings-general.ts + settings-appearance.ts + settings-profile.ts + settings-security.ts + settings-ai.ts + settings-language.ts + settings-languages.ts + portal-common.ts + portal-dashboard.ts + portal-projects.ts + portal-project-detail.ts + portal-tasks.ts + portal-revisions.ts + portal-settings.ts validation.ts - -server/repositories/ - i18n.ts - content-translations.ts - -components/i18n/ - i18n-provider.tsx - locale-tabs.tsx - localized-fields.tsx - translation-status.tsx + status.ts ``` -### 5.2. Namespace standardı +### 6.2. Katalog kalite kapıları -Katalog tek ve devasa bir JSON dosyası olmayacaktır. Anahtarlar modül bazında -namespace'lere ayrılır: +- `tr` ve `en` key parity yüzde 100 olmalıdır. +- Boş string çeviri sayılmaz. +- Interpolation değişkenleri iki dilde aynı olmalıdır. +- Raw key production UI'da görünmemelidir. +- Built-in katalogda bir dilin metni diğer dile kopyalanmış olmamalıdır. +- Her sayfanın kendi missing-key smoke testi olmalıdır. +- Hard-coded kullanıcı metni taraması sayfa fazının testine eklenmelidir. +- Teknik log ve geliştirici hata detayları taramanın dışında tutulabilir. -| Namespace | Kapsam | -| --- | --- | -| `common` | Kaydet, sil, düzenle, iptal, loading, boş durumlar | -| `auth` | Login, register, davet, şifre ve auth hataları | -| `navigation` | Freelancer ve portal sidebar | -| `dashboard` | Freelancer dashboard | -| `clients` | Müşteri liste/detay/form | -| `projects` | Proje liste/detay/form/planlama | -| `tasks` | Görev liste/kanban/form | -| `calendar` | Takvim ve etkinlik | -| `finance` | Finans, analiz ve işlem formu | -| `journal` | Günlük ekranı | -| `chat` | AI sohbet ekranı | -| `settings` | Ayarlar ve dil yönetimi | -| `portal` | Müşteri portalı | -| `status` | Domain status/priority/type label'ları | -| `validation` | Form, action ve kullanıcıya açık hata metinleri | +## 7. Dinamik içerik çevirisi sözleşmesi -Her anahtar anlamlı ve stabil olmalıdır. Türkçe cümle anahtar olarak -kullanılmamalıdır. +Arayüz metni ile kullanıcının oluşturduğu domain içeriği ayrı kalacaktır. -## 6. Locale çözümleme sözleşmesi +### 7.1. Form davranışı -### 6.1. Locale formatı - -- Locale kodları BCP 47 formatında kabul edilir: `tr`, `en`, `fr`, `de`, - `pt-BR`, `zh-CN`. -- Girdi `Intl.getCanonicalLocales()` ile normalize edilir. -- Aynı locale farklı harf biçimleriyle ikinci kez eklenemez. -- Locale kaydında gösterim adı, native ad ve yön (`ltr`/`rtl`) tutulur. -- Maksimum locale kodu ve metin uzunlukları merkezi Zod schema ile doğrulanır. - -### 6.2. Freelancer locale önceliği - -1. Giriş yapan kullanıcının `user_preferences.language` değeri -2. Güvenli ve aktif bir `neta_locale` cookie değeri -3. Instance varsayılan locale'i -4. Son güvenlik fallback'i olarak `tr` - -### 6.3. Müşteri portalı locale önceliği - -1. Müşteri hesabının `user_preferences.language` değeri -2. İlgili `clients.portal_locale` değeri -3. Instance varsayılan locale'i -4. Son güvenlik fallback'i olarak `tr` - -### 6.4. Davet ve auth öncesi locale önceliği - -1. Portal davetinin `locale` snapshot'ı -2. Aktif `neta_locale` cookie değeri -3. Instance varsayılan locale'i -4. Son güvenlik fallback'i olarak `tr` - -`Accept-Language` yalnızca public login ekranında, header'daki locale instance -üzerinde aktifse düşük öncelikli ilk ziyaret ipucu olarak kullanılabilir. Müşteri -davetinin seçilmiş dilini değiştiremez. - -### 6.5. Çeviri fallback zinciri - -Örnek seçili locale `fr-CA` ise: - -```text -fr-CA override - -> fr override - -> locale kaydında seçilen fallback (en veya tr) - -> built-in en - -> kontrollü missing-translation metni -``` - -Production ekranında `projects.form.name.label` gibi ham anahtarlar -gösterilmeyecektir. Development ve test ortamında missing key loglanacaktır. - -### 6.6. HTML ve formatlama - -- Root layout içindeki `lang` aktif locale'e göre üretilecek. -- Root layout içindeki `dir`, locale kaydındaki `ltr` veya `rtl` değerinden - üretilecek. -- Tarih/saat `Intl.DateTimeFormat(locale, options)` ile biçimlendirilecek. -- Para ve sayı `Intl.NumberFormat(locale, options)` ile biçimlendirilecek. -- `date-fns` gereken yerlerde locale mapping merkezi helper üzerinden seçilecek. -- Timezone tercihi locale'den bağımsız kalacak. -- Para birimi tercihi locale'den bağımsız kalacak. - -## 7. Hedef veri modeli - -Tablo ve kolon adları implementasyon sırasında Drizzle naming standardına göre -kesinleştirilir. - -### 7.1. `instance_locales` - -Instance üzerinde yönetilebilen dil listesidir. - -| Alan | Amaç | -| --- | --- | -| `code` | Canonical BCP 47 locale, primary key | -| `display_name` | Ayarlar ekranındaki yönetim adı | -| `native_name` | Dilin kendi dilindeki adı | -| `direction` | `ltr` veya `rtl` | -| `fallback_locale` | Eksik anahtarların düşeceği aktif locale | -| `status` | `draft`, `active`, `archived` | -| `is_builtin` | Türkçe ve İngilizce koruması | -| `sort_order` | Form tab sırası | -| `created_at` | Oluşturulma zamanı | -| `updated_at` | Güncellenme zamanı | - -Kurallar: - -- `tr` ve `en` migration ile eklenir. -- Built-in locale silinemez veya arşivlenemez. -- Varsayılan veya müşteri tarafından kullanılan locale doğrudan silinemez. -- Locale silmek yerine arşivleme tercih edilir. -- `fallback_locale`, kaydın kendisi olamaz ve döngü oluşturamaz. - -### 7.2. `instance_i18n_settings` - -Tek instance'a ait global dil ayarlarıdır. - -| Alan | Amaç | -| --- | --- | -| `id` | Sabit `default` kaydı | -| `owner_user_id` | Instance sahibi | -| `default_locale` | Yeni kullanıcılar ve genel fallback | -| `catalog_version` | Cache invalidation için artan sürüm | -| `updated_by_user_id` | Son değiştiren owner | -| `updated_at` | Son değişiklik | - -İlk migration `default_locale=tr` olarak seed eder. - -### 7.3. `instance_ui_translations` - -Built-in katalog override'larını ve sonradan eklenen locale metinlerini saklar. - -| Alan | Amaç | -| --- | --- | -| `id` | Teknik kimlik | -| `locale` | `instance_locales.code` ilişkisi | -| `namespace` | Katalog namespace'i | -| `translation_key` | Stabil anahtar | -| `value` | Çevrilmiş düz metin | -| `updated_by_user_id` | Değişikliği yapan owner | -| `created_at` | Oluşturulma zamanı | -| `updated_at` | Güncellenme zamanı | - -Unique constraint: - -```text -(locale, namespace, translation_key) -``` - -Built-in Türkçe ve İngilizce metinler kodda kalır. Bu tablo yalnızca değiştirilen -değerleri ve custom locale değerlerini taşır; böylece her katalog sürümünde -binlerce seed satırı oluşturulmaz. - -### 7.4. `content_translations` - -Portalda gösterilebilen domain içeriklerini normalize biçimde saklar. - -| Alan | Amaç | -| --- | --- | -| `id` | Teknik kimlik | -| `owner_user_id` | Authorization scope | -| `entity_type` | `project`, `task`, `planning_section`, `branding` vb. | -| `entity_id` | İlgili domain kaydı | -| `field` | `name`, `title`, `description`, `content`, `cover_image_alt` vb. | -| `locale` | İçeriğin locale'i | -| `value` | Çevrilmiş metin | -| `created_at` | Oluşturulma zamanı | -| `updated_at` | Güncellenme zamanı | - -Unique constraint: - -```text -(owner_user_id, entity_type, entity_id, field, locale) -``` - -Polymorphic `entity_id` nedeniyle DB seviyesinde her domain tablosuna foreign key -kurulamaz. Bütün erişim merkezi `ContentTranslationService` üzerinden yapılır: - -- Geçerli entity ve çevrilebilir alan registry üzerinden doğrulanır. -- Owner scope zorunludur. -- Domain kaydı silinirken çeviri kayıtları aynı transaction içinde silinir. -- Portal actor yalnızca kendisine açık kaynakların resolved metnini okuyabilir. -- Portal actor ham çeviri setini veya başka locale içeriklerini değiştiremez. - -### 7.5. Mevcut tablo değişiklikleri - -- `user_preferences.language` üzerindeki sabit `in ('tr', 'en')` constraint'i - kaldırılır. -- Bu alan yalnızca instance üzerinde aktif locale değerlerini service katmanında - kabul eder. -- `clients.portal_locale` alanı eklenir. -- `portal_invitations.locale` alanı eklenir. -- Davet kabul transaction'ında client locale ve yeni kullanıcının preference - locale'i birlikte yazılır. -- Gerekirse mevcut `owner_user_id` adlı preference kolonu şema kırmadan korunur; - kod tarafında bunun tüm auth kullanıcıları için preference sahibi anlamına - geldiği belgelenir. - -### 7.6. Eski içerik kolonları - -`projects.name`, `projects.description`, `tasks.title` gibi mevcut kolonlar ilk -sürümde kaldırılmayacaktır. - -- Migration mevcut değerleri Türkçe translation kayıtlarına backfill eder. -- Yeni create/update akışı varsayılan locale değerini hem eski kolona hem - `content_translations` tablosuna transaction içinde yazar. -- Eski kolonlar geriye uyumluluk ve güvenli rollback projection'ı olur. -- Portal ve yeni API okuma akışları locale-aware resolver kullanır. -- Bu kolonların tamamen kaldırılması ayrı bir major veri modeli kararıdır. - -## 8. Çevrilebilir domain alanları - -### 8.1. İlk zorunlu kapsam - -| Entity | Çevrilebilir alanlar | Portal etkisi | -| --- | --- | --- | -| `project` | `name`, `description`, `coverImageAlt` | Proje liste ve detay | -| `projectPlanningSection` | `title`, `content` | Proje planlama içeriği | -| `task` | `title`, `description` | Yalnız müşteriye açık görevler | -| `branding` | `portalWelcomeText`, `portalFooterText` | Portal shell/dashboard | - -### 8.2. İkinci kapsam - -| Entity | Çevrilebilir alanlar | Not | -| --- | --- | --- | -| `proposal` | `title`, `description` | Portalda yayınlandığında zorunlu | -| `contract` | `title`, `content` | Portalda yayınlandığında zorunlu | -| `invoice` | Açıklama/not alanı eklenirse | Mevcut şemada metin alanı yok | -| `calendarEvent` | `title`, `description` | Portal görünürlüğü eklenirse | - -### 8.3. Çevrilmeyecek domain alanları - -- Client adı ve firma adı kimlik verisidir; locale tab'ına girmez. -- Finans açıklaması owner-only olduğu sürece tek dilde kalır. -- Günlük notları ve AI chat içerikleri tek dilde kalır. -- Müşteri aktivite başlığı/içeriği owner-only olduğu sürece tek dilde kalır. -- Revizyon talebi müşterinin yazdığı dilde saklanır. - -Bu kapsam ileride portal görünürlüğü değiştiğinde registry üzerinden -genişletilebilir. - -## 9. Çok dilli form UX sözleşmesi - -Formlar Poyraz UI `Tabs` bileşeniyle ortak bir `LocalizedFields` bileşimi -kullanacaktır. - -Örnek proje formu: - -```text -[ Türkçe ✓ ] [ English ! ] [ Français ✓ ] - -Proje adı -[ Marka web sitesi ] - -Açıklama -[ ... ] - ------------------------------------------------- -Tür, müşteri, durum, tarih, bütçe, para birimi -``` - -Kurallar: - -- Aktif diller instance `sort_order` değerine göre tab olarak gösterilir. -- Varsayılan dil ilk tab olur ve “Varsayılan” işareti taşır. -- Eksik zorunlu alan bulunan tab uyarı noktası taşır. -- Dolu tab görsel tamamlanma işareti taşır. +- Form açıldığında tüm `active` diller, instance sırasına göre tab olur. +- Instance default dili ilk tab ve zorunlu kaynaktır. +- Tab label'ında native name, default rozeti ve eksik alan göstergesi yer alır. +- Yalnız çevrilebilir metin alanları tab paneline girer. +- Tarih, tutar, durum, ilişki, checkbox, dosya ve teknik alanlar tab'ların dışında + tek kez gösterilir. - Tab değiştirmek form state'ini kaybettirmez. -- Input ID'leri locale ile ayrıştırılır. -- Form field adları merkezi parser'ın okuyacağı stabil yapıda olur: - `translations.tr.name`, `translations.en.name` gibi. -- Zorunlu alan doğrulaması varsayılan locale için yapılır. -- Müşteriye açık içerik kaydedilirken hedef portal locale'de eksik alan varsa - kullanıcıya fallback uygulanacağı açıkça gösterilir. -- Dil ekleme veya arşivleme açık bir formu bozmaz; form açılırken locale snapshot'ı - alınır. -- Mobil ve dar ekranlarda tab listesi yatay kayabilir; formun tamamı yatay scroll - oluşturmaz. -- Screen reader için tab, panel, label ve validation ilişkileri korunur. +- Create ve edit aynı ortak `LocalizedFormSection` sözleşmesini kullanır. +- Edit formu her dilde kayıtlı değeri ayrı yükler; resolved fallback değerini + gerçek kayıt gibi input'a yazmaz. +- Fallback ile gösterilen değer input'ta preview olabilir fakat “bu dilde kayıtlı” + gibi işaretlenmez. +- Default dilde zorunlu alanlar kaydı engeller. +- Diğer aktif dillerde eksik alanlar tab üzerinde gösterilir; ürün kuralına göre + warning veya portal publish blocker olur. +- Form action bütün locale payload'unu tek transaction içinde kaydeder. +- Domain kaydı silinince ilgili `content_translations` kayıtları da silinir. -## 10. Dil yönetimi UX sözleşmesi +### 7.2. Çevrilebilir alan matrisi -Ayarlar sayfasına `Diller ve çeviriler` bölümü eklenir. +| Entity | Çevrilecek alanlar | Çevrilmeyecek alan örnekleri | +| --- | --- | --- | +| Branding | `portalWelcomeText`, `portalFooterText` | logo dosyaları, renk | +| Client | `notes` | kişi/firma adı, e-posta, telefon | +| Client activity | `title`, `content` | activity type, tarih | +| Project | `name`, `description`, `coverImageAlt` | bütçe, tarih, status | +| Planning section | `title`, `content` | category, sort order | +| Task | `title`, `description` | status, priority, süre | +| Calendar event | `title`, `description` | başlangıç/bitiş, type | +| Finance transaction | `category`, `description` | tutar, para birimi, ödeme durumu | +| Journal entry | `moodLabel`, `note` | skorlar, tarih | +| Proposal | `title`, `description` | tutar, currency, status | +| Contract | `title`, `content` | status ve ilişkiler | +| Subscription | `name`, `category` | tutar, billing cycle, tarih | -### 10.1. Dil listesi +### 7.3. Kaynak dilinde saklanacak iletişim içeriği -Her dil satırında: +Aşağıdaki içerikler çeviri tab'ıyla çoğaltılmaz; çünkü bir kişinin yazdığı +mesajdır: -- Native ad ve locale kodu -- Built-in/custom bilgisi -- Draft/active/archived durumu -- Genel çeviri tamamlanma yüzdesi -- Portal kritik çeviri tamamlanma yüzdesi -- Varsayılan dil işareti -- Düzenle, dışa aktar, aktif et/arşivle aksiyonları +- Chat mesajı +- Portal revizyon talebi +- Gelecekte e-posta/yorum/mesaj kayıtları -### 10.2. Dil ekleme +Bu kayıtlara `sourceLocale` eklenebilir. Otomatik çeviri ayrı bir özellik olur ve +orijinal metni değiştirmez. Chat session'ın kullanıcı tarafından düzenlenebilen +başlığı için ise aktif dil tab'ları kullanılabilir; salt AI tarafından üretilen +başlıkta source locale saklanır. -Dil ekleme dialog'unda: +### 7.4. Legacy kolon ve translation tablosu -- Locale kodu -- Görünen ad -- Native ad -- Yazı yönü -- Fallback dili +- Mevcut ana metin kolonları bu sürümde kaldırılmaz. +- Default locale değeri legacy kolona ve `content_translations` kaydına aynı + transaction içinde yazılır. +- İlk migration eksik entity/field kayıtlarını instance default locale'e + backfill eder. +- Backfill idempotent olur ve kullanıcı çevirisini ezmez. +- Read tarafı requested locale -> locale fallback -> instance default -> legacy + kolon zincirini kullanır. +- Liste sorgularında N+1 yapılmaz; translation kayıtları entity ID listesiyle + batch okunur. -yer alır. Yeni dil `draft` oluşturulur ve henüz müşterilere atanamaz. +## 8. Teknik mimari düzeltmeleri -### 10.3. Çeviri editörü +### 8.1. Resolver'ları ayır -- Namespace filtresi -- Anahtar veya kaynak metin arama -- Yalnız eksik çevirileri gösterme -- Türkçe ve İngilizce referans metinlerini yan yana görme -- Hedef locale input'u -- Tek alan kaydetme ve toplu kaydetme -- Fallback'ten gelen değer ile gerçek override ayrımını görme -- Değeri sıfırlayarak built-in/fallback metnine dönme -- Portal için kritik anahtar filtresi +Tek `resolveRequestLocale()` fonksiyonu bütün bağlamları tahmin etmeye +çalışmayacaktır. -### 10.4. JSON içe/dışa aktarma - -Önerilen format: - -```json -{ - "schemaVersion": 1, - "locale": "fr", - "fallbackLocale": "en", - "catalogVersion": 1, - "translations": { - "common.save": "Enregistrer", - "navigation.projects": "Projets" - } -} +```text +resolvePublicLocale() +resolveInvitationLocale(token) +resolveFreelancerLocale(session) +resolvePortalLocale(session, client) ``` -İçe aktarma: - -- Owner yetkisi gerektirir. -- Dosya boyutu ve entry sayısı sınırlandırılır. -- Bilinmeyen key'ler raporlanır ve varsayılan olarak yazılmaz. -- Geçersiz veya tehlikeli anahtarlar reddedilir. -- Mevcut değerlerin üzerine yazma için açık onay gerekir. -- Import özeti ekleme/değiştirme/atlama sayılarıyla gösterilir. - -## 11. Portal dili ve davet akışı - -Yeni davet akışı: - -1. Freelancer müşteri detayında “Portal hesabı aç” dialog'unu açar. -2. E-posta ile birlikte `Portal dili` seçer. -3. Dropdown yalnızca aktif ve portal kritik çevirileri hazır dilleri gösterir. -4. Seçim `portal_invitations.locale` alanına snapshot olarak yazılır. -5. Aynı seçim `clients.portal_locale` alanına kaydedilir. -6. Müşteri davet linkini açtığında hesap oluşturma ekranı bu dilde görünür. -7. Davet kabul transaction'ı Better Auth user, profile, client bağlantısı ve - `user_preferences.language` kaydını atomik oluşturur. -8. Müşteri login olduğunda portal aynı locale ile açılır. -9. Freelancer daha sonra müşteri detayından portal dilini değiştirebilir. -10. Dil arşivlenecekse bu dili kullanan müşteriler için yeni locale seçilmeden - işlem tamamlanmaz. - -Davet yenilendiğinde yeni davet seçilen son locale'i taşır. Eski davet revoke -edilirken locale audit metadata'sına eklenir. - -## 12. API ve mobil istemci sözleşmesi - -Çok dillilik yalnızca web UI davranışı olmayacaktır. - -### 12.1. Discovery ve metadata - -`/api/v1/meta` additive olarak aşağıdaki bilgileri döndürür: - -```json -{ - "localization": { - "defaultLocale": "tr", - "supportedLocales": [ - { - "code": "tr", - "nativeName": "Türkçe", - "direction": "ltr" - }, - { - "code": "en", - "nativeName": "English", - "direction": "ltr" - } - ] - } -} -``` - -Capability listesine aşağıdaki kayıt eklenir: - -```json -{ - "id": "instance.localization", - "version": 1, - "status": "available", - "access": "public" -} -``` - -### 12.2. Kullanıcı bilgisi - -`GET /api/v1/me`: - -- Çözülmüş `preferences.language` değerini döndürür. -- Client rolünde `portalLocale` bilgisini döndürür. -- Locale'in arşivlenmesi gibi edge case'lerde çözülmüş fallback locale'i döndürür. - -### 12.3. Gelecek resource endpoint'leri - -- `Accept-Language` destekler. -- Gerekirse açık `?locale=fr` parametresi destekler. -- Client actor yalnızca kendi atanmış/izin verilen locale görünümünü alır. -- Freelancer actor isterse `includeTranslations=true` ile bütün edit setini alır. -- Varsayılan resource response resolved metni döndürür. -- Mutation sözleşmesi `translations: { locale: { field: value } }` yapısını +- Public resolver cookie ve `Accept-Language` kullanmaz. +- Freelancer resolver user preference kullanır. +- Portal resolver user preference ve client default ayrımını korur. +- Invitation resolver yalnız geçerli invitation snapshot ve instance fallback kullanır. -- Bilinmeyen locale için stabil `UNSUPPORTED_LOCALE` hata kodu döner. -- API'nin `error.code` alanı stabil kalır; `error.message` seçili locale'de - üretilebilir. - -### 12.4. Cache sözleşmesi - -- Public meta cache'i dil listesi değişince revalidate edilir. -- Locale bağımlı public yanıtlar `Vary: Accept-Language` taşır. -- Authenticated resource yanıtları `private, no-store` kalır. -- Mobil istemci katalog sürümünü saklayabilir; değiştiğinde ilgili locale - sözlüğünü yeniler. - -## 13. Güvenlik ve veri bütünlüğü - -- Locale ve çeviri yönetimi yalnızca freelancer/owner rolüne açıktır. -- UI translation değerleri varsayılan olarak düz metin render edilir. -- Çeviri editörü key üzerinden key dışı kod veya HTML çalıştırmaz. -- Rich text gerekirse ayrı sanitize edilmiş alan tipi olarak tasarlanır. -- Interpolation yalnızca tanımlı değişkenlere izin verir. -- Kullanıcı tarafından girilen `{{...}}` token'ları katalog şemasına göre - doğrulanır. -- Her çeviri değeri için maksimum uzunluk tanımlanır. -- JSON import için byte ve entry limiti uygulanır. -- `__proto__`, `constructor`, path traversal benzeri key'ler reddedilir. -- Fallback döngüleri service katmanında engellenir. -- Aktif kullanımda olan locale hard delete edilemez. -- İçerik çevirileri her read/write işleminde owner ve actor scope'u ile - sınırlandırılır. -- Client başka müşteriye ait translation kayıtlarını enumerate edemez. -- Davet locale'i sadece aynı owner'ın aktif locale listesinden seçilebilir. -- Locale değişiklikleri auth audit veya ayrı settings audit kaydına yazılır. - -## 14. Performans ve cache yaklaşımı - -- Built-in `tr/en` katalogları statik import ile gelir. -- DB yalnızca override ve custom locale değerlerini taşır. -- Server translator, request başına resolved dictionary oluşturur. -- Client layout'a yalnız ihtiyaç duyduğu namespace'leri alır. -- Tüm katalog her sayfada browser'a gönderilmez. -- `catalog_version` her locale/translation mutation'ında transaction içinde - artırılır. -- Cache anahtarı `locale + namespace set + catalogVersion` olur. -- Dil değişikliğinde ilgili layout ve metadata revalidate edilir. -- İçerik resolver liste sorgularında N+1 oluşturmaz; entity ID listesi ve locale - zinciri için tek toplu sorgu kullanır. -- Portal proje/görev listelerinde translation join/batch ölçümü yapılır. -- SQLite index'leri locale ve entity lookup'larına göre eklenir. - -## 15. Faz bazlı uygulama planı - -### Faz 0 — Envanter, ADR ve test fixture'ları - -Amaç: Kod yazmadan önce kapsamı ve davranış sözleşmelerini kilitlemek. - -#### Görevler - -- [x] Kullanıcıya görünen sabit metinlerin dosya ve namespace envanterini çıkar. -- [x] Freelancer ve portal sayfalarını ayrı migration listelerine ayır. -- [x] Tüm sabit `tr-TR`, `` ve `date-fns/locale/tr` - kullanımlarını kaydet. -- [x] Çevrilebilir domain alan registry'sini kesinleştir. -- [x] UI çevirisi ile domain içerik çevirisi ayrımını ADR olarak yaz. -- [x] Locale çözümleme önceliğini ADR olarak yaz. -- [x] Route prefix kullanılmaması kararını kaydet. -- [x] Custom runtime i18n katmanı kararını ve bağımlılık etkisini kaydet. -- [x] Türkçe mevcut veri fixture'ı hazırla. -- [x] İngilizce ve eksik Fransızca katalog fixture'ı hazırla. -- [x] Freelancer, Türkçe client ve İngilizce client auth fixture'ları hazırla. -- [x] RTL davranışı için test locale'i belirle. -- [x] Baseline build, typecheck ve ilgili smoke sonuçlarını kaydet. - -#### Faz 0 çıktıları - -- [Faz 0 ADR seti](i18n-phase-0/adr.md) -- [Faz 0 i18n envanteri](i18n-phase-0/inventory.md) -- [Faz 0 fixture planı](i18n-phase-0/fixtures.md) -- [Faz 0 migration ve geri dönüş sözleşmesi](i18n-phase-0/migration-contract.md) -- [Faz 0 baseline sonuçları](i18n-phase-0/baseline.md) - -#### Çıkış kriteri - -- [x] Katalog anahtar standardı onaylandı. -- [x] İlk çevrilebilir entity/field listesi onaylandı. -- [x] Fallback ve portal yayın kuralları tartışmasız hale geldi. -- [x] Migrasyon öncesi geri dönüş ve backup adımları yazıldı. - -### Faz 1 — Locale veri modeli ve service katmanı - -Amaç: Dil yönetiminin güvenli SQLite temelini oluşturmak. - -#### Görevler - -- [x] `instance_locales` Drizzle şemasını ekle. -- [x] `instance_i18n_settings` Drizzle şemasını ekle. -- [x] `instance_ui_translations` Drizzle şemasını ekle. -- [x] `content_translations` Drizzle şemasını ekle. -- [x] `clients.portal_locale` alanını ekle. -- [x] `portal_invitations.locale` alanını ekle. -- [x] `user_preferences.language` sabit check constraint'ini kaldır. -- [x] Gerekli unique constraint ve index'leri ekle. -- [x] Türkçe ve İngilizce locale seed'lerini migration içine ekle. -- [x] Instance varsayılan dilini `tr` olarak seed et. -- [x] Mevcut preference kayıtlarını doğrula ve normalize et. -- [x] Locale CRUD repository ve service katmanını yaz. -- [x] Locale ekleme, aktif etme, arşivleme ve default değiştirme validasyonlarını - yaz. -- [x] Fallback döngüsü ve referanslı locale korumasını uygula. -- [x] Owner/client negatif authorization testlerini yaz. -- [x] Backup/restore smoke testine yeni tabloları dahil et. - -#### Faz 1 çıktıları - -- [Faz 1 locale veri modeli ve service raporu](i18n-phase-1.md) -- `server/db/schema/i18n.ts` -- `server/repositories/i18n.ts` -- `server/i18n/service.ts` -- `server/db/migrations/0008_wild_mercury.sql` -- `scripts/i18n-phase1-smoke.mjs` -- `scripts/i18n-phase1-smoke.ts` - -#### Çıkış kriteri - -- [x] Mevcut veritabanı kayıpsız migrate oluyor. -- [x] Yeni instance `tr` ve `en` ile açılıyor. -- [x] Geçersiz locale ve fallback döngüsü DB'ye yazılamıyor. -- [x] Aktif kullanılan locale yanlışlıkla silinemiyor. -- [x] Mevcut login, dashboard ve portal akışları değişmeden çalışıyor. - -### Faz 2 — Çeviri runtime'ı ve built-in kataloglar - -Amaç: Server ve client component'lerde ortak, typed ve fallback destekli çeviri -altyapısını kurmak. - -#### Görevler - -- [x] Katalog key/namespace tiplerini tanımla. -- [x] Built-in Türkçe katalogları mevcut metinlerden çıkar. -- [x] İngilizce katalogları eksiksiz hazırla. -- [x] Server-side `createTranslator(locale, namespaces)` helper'ını yaz. -- [x] Client `I18nProvider` ve `useTranslations` hook'unu yaz. -- [x] Interpolation formatını ve doğrulamasını uygula. -- [x] Basit çoğul kurallarını `Intl.PluralRules` ile uygula. -- [x] DB override + built-in + fallback merge sırasını uygula. -- [x] Locale resolver'ı cookie/session/instance kaynaklarına bağla. -- [x] `neta_locale` cookie lifecycle'ını ekle. -- [x] Root layout `lang` ve `dir` değerlerini dinamik yap. -- [x] Ortak tarih, saat, para ve sayı formatter'larını yaz. -- [x] Missing key development log'u ve production fallback davranışını uygula. -- [x] Namespace bazlı cache ve `catalog_version` invalidation'ını uygula. -- [x] Türkçe/İngilizce katalog parity testini yaz. - -#### Faz 2 çıktıları - -- [Faz 2 çeviri runtime raporu](i18n-phase-2.md) -- `lib/i18n/*` -- `locales/tr/*` -- `locales/en/*` -- `components/i18n/i18n-provider.tsx` -- `server/i18n/catalog.ts` -- `server/i18n/locale.ts` -- `server/i18n/resolver.ts` -- `server/i18n/translator.ts` -- `server/i18n/runtime.ts` -- `app/api/i18n/locale/route.ts` -- `scripts/i18n-phase2-smoke.mjs` -- `scripts/i18n-phase2-smoke.ts` - -#### Çıkış kriteri - -- [x] Aynı component Türkçe ve İngilizce render edilebiliyor. -- [x] Custom locale override deployment olmadan okunabiliyor. -- [x] Eksik Fransızca anahtar belirlenen fallback ile gösteriliyor. -- [x] `` ve `` locale'e göre doğru. -- [x] Client hydration mismatch oluşmuyor. -- [x] Built-in katalog key setleri yüzde 100 eşleşiyor. - -### Faz 3 — Ayarlar: dil ve çeviri yönetimi - -Amaç: Instance sahibinin kod değiştirmeden dil ekleyip yönetebilmesini sağlamak. - -#### Görevler - -- [x] Ayarlar sidebar'ına `Diller ve çeviriler` bölümü ekle. -- [x] Dil listesi ve tamamlanma kartlarını Poyraz UI ile oluştur. -- [x] Yeni dil ekleme dialog'unu ekle. -- [x] Default locale değiştirme akışını ekle. -- [x] Giriş yapan freelancer için kişisel arayüz dili seçicisini ekle ve - `user_preferences.language` alanına bağla. -- [x] Draft/active/archive lifecycle'ını ekle. -- [x] Namespace ve eksik anahtar filtreli çeviri editörünü ekle. -- [x] Türkçe/İngilizce referans metinlerini editörde göster. -- [x] Tekil ve toplu çeviri kaydetme action'larını yaz. -- [x] Built-in override'ı sıfırlama aksiyonunu ekle. -- [x] Genel ve portal kritik tamamlanma yüzdesini hesapla. -- [x] JSON export endpoint/action'ını yaz. -- [x] Güvenli JSON import preview ve commit akışını yaz. -- [x] Çeviri değişikliklerinde layout/meta/cache revalidation uygula. -- [x] Ayarlar mutation'larını audit et. -- [x] Yetkisiz client erişimi için negatif test ekle. - -#### Faz 3 çıktıları - -- [Faz 3 ayarlar dil ve çeviri yönetimi raporu](i18n-phase-3.md) -- `app/(dashboard)/settings/page.tsx` -- `app/(dashboard)/settings/actions.ts` -- `server/i18n/service.ts` -- `server/repositories/i18n.ts` -- `server/settings/preferences.ts` -- `scripts/i18n-phase3-smoke.mjs` -- `scripts/i18n-phase3-smoke.ts` - -#### Çıkış kriteri - -- [x] Owner Ayarlar'dan `fr` ekleyebiliyor. -- [x] Fransızca bir sidebar metni girilip sayfa yenilemeden sonra kullanılabiliyor. -- [x] Eksik metinler editörde bulunabiliyor. -- [x] JSON export/import round-trip veri kaybetmiyor. -- [x] Client dil yönetimi endpoint/action'larına erişemiyor. - -### Faz 4 — Freelancer arayüzünün sayfa sayfa taşınması - -Amaç: Owner uygulamasındaki bütün sistem metinlerini katalog üzerinden göstermek. - -#### Ortak bileşenler - -- [x] App shell, skip link ve mobil menü -- [x] Sidebar grup ve menüleri -- [x] Hesap dropdown'ı ve çıkış -- [x] Page header, stat card ve empty/error state -- [x] Status badge ve confirmation dialog -- [x] Toast, pending button ve genel validation mesajları -- [x] Metadata description ve erişilebilirlik label'ları - -#### Sayfalar - -- [x] Login -- [x] İlk admin kaydı -- [x] Dashboard -- [x] Takvim -- [x] Analizler -- [x] Müşteriler listesi -- [x] Müşteri detayı ve portal daveti -- [x] Projeler listesi -- [x] Proje detayı -- [x] Görevler -- [x] Finans -- [x] Günlük -- [x] AI sohbet -- [x] Teklifler -- [x] Faturalar -- [x] Abonelikler -- [x] Ayarlar - -#### Formatlama ve hata sözleşmesi - -- [x] Tüm sabit `tr-TR` kullanımını locale-aware formatter'a taşı. -- [x] Tüm sabit `date-fns` Türkçe locale importlarını merkezi mapping'e taşı. -- [x] Enum değerlerini çevirmeden, yalnız gösterim label'larını çevir. -- [x] Server action'larda kullanıcıya gösterilen string yerine stabil hata kodu - döndürmeye başla. -- [x] Hata kodlarını `validation` namespace'i üzerinden kullanıcı locale'inde - göster. -- [x] AI endpoint teknik hata detaylarını korurken UI mesajlarını locale-aware yap. - -#### Faz 4 çıktıları - -- [Faz 4 freelancer UI migration raporu](i18n-phase-4.md) -- [Faz 4 kalan hardcoded metin raporu](i18n-phase-4-hardcoded-text-report.md) -- `components/layout/app-shell.tsx` -- `components/layout/dashboard-shell.tsx` -- `config/sidebar.ts` -- `app/(dashboard)/layout.tsx` -- `app/(dashboard)/dashboard-client.tsx` -- `lib/i18n/browser.ts` -- `lib/i18n/date-fns.ts` -- `scripts/i18n-phase4-boundary.mjs` - -#### Çıkış kriteri - -- [x] Freelancer UI Türkçe ve İngilizce eksiksiz kullanılabiliyor. -- [x] Sidebar ve tüm header'lar seçili dilde. -- [x] Dialog, toast, empty state ve form doğrulamaları seçili dilde. -- [x] Tarih, sayı ve para formatları seçili locale'e uyuyor. -- [x] Hardcoded kullanıcı metni boundary kontrolü kalan istisnaları raporluyor. - -### Faz 5 — Çok dilli domain içerikleri ve form tab'ları - -Amaç: Freelancer'ın müşteriye gösterilecek içeriği aktif dillerde girebilmesini -sağlamak. - -#### Altyapı - -- [x] Çevrilebilir entity/field registry'sini kodla. -- [x] `ContentTranslationService` ve repository batch sorgularını yaz. -- [x] Locale fallback'li content resolver'ı yaz. -- [x] Create/update/delete transaction entegrasyonunu yaz. -- [x] `LocalizedFields` ve `LocaleTabs` ortak bileşenlerini yaz. -- [x] FormData translation parser ve Zod doğrulamasını yaz. -- [x] Eksik tab göstergesi ve default locale zorunluluğunu ekle. -- [x] Mevcut Türkçe içeriği `tr` kayıtlarına idempotent backfill et. -- [x] Eski temel kolon ile varsayılan locale projection'ını transaction içinde - senkron tut. - -#### Formlar - -- [x] Proje oluşturma formu: ad, açıklama, görsel alt metni -- [x] Proje düzenleme formu: ad, açıklama, görsel alt metni -- [x] Planlama alanı oluşturma/düzenleme: başlık ve içerik -- [x] Görev oluşturma/düzenleme: başlık ve açıklama -- [x] Proje detayından görev ekleme: başlık ve açıklama -- [x] Genel ayarlar: portal karşılama ve footer metni -- [x] İkinci kapsam aktifse teklif ve sözleşme formları — bu fazda aktif - kapsam dışı bırakıldı. - -#### Okuma akışları - -- [x] Freelancer liste/detayında kendi locale'ine resolved metin göster. -- [x] Edit formlarında tüm locale değerlerini batch yükle. -- [x] Arama davranışını tanımla: ilk sürümde aktif owner locale + temel kolon. -- [x] AI context üretirken source/default locale davranışını belirle. -- [x] İçerik silinince translation kayıtlarını aynı transaction'da sil. - -#### Çıkış kriteri - -- [x] Proje adı Türkçe, İngilizce ve Fransızca ayrı kaydedilebiliyor. -- [x] Tab değişimi girilmiş değeri kaybettirmiyor. -- [x] Varsayılan dil başlığı boş proje oluşturulamıyor. -- [x] Aynı proje seçili locale'e göre farklı resolved başlık döndürüyor. -- [x] Locale translation query'leri liste ekranında N+1 oluşturmuyor. -- [x] Eski veriler migration sonrasında Türkçe olarak görünmeye devam ediyor. - -#### Faz 5 çıktıları - -- [Faz 5 çok dilli domain içerikleri raporu](i18n-phase-5.md) -- `lib/i18n/content.ts` -- `server/i18n/content.ts` -- `components/i18n/localized-fields.tsx` -- `app/(dashboard)/projects/actions.ts` -- `app/(dashboard)/tasks/actions.ts` -- `app/(dashboard)/projects/page.tsx` -- `app/(dashboard)/projects/[id]/page.tsx` -- `app/(dashboard)/tasks/page.tsx` -- `scripts/i18n-phase5-smoke.mjs` -- `scripts/i18n-phase5-smoke.ts` - -### Faz 6 — Müşteri portal dili ve davet akışı - -Amaç: Müşterinin ilk davet ekranından itibaren kendisine atanmış dili görmesini -sağlamak. - -#### Davet - -- [x] Portal hesabı açma dialog'una dil dropdown'u ekle. -- [x] Yalnız portal-ready aktif dilleri seçilebilir yap. -- [x] Compatibility `/api/create-client-user` adapter'ına locale ekle. -- [x] `/api/portal-invitations` sözleşmesine locale ekle. -- [x] Invitation schema ve service validasyonuna locale ekle. -- [x] Locale'i invitation ve client kaydına transaction içinde yaz. -- [x] Davet preview response'una güvenli locale bilgisini ekle. -- [x] Davet sayfasını invitation locale'i ile render et. -- [x] Davet kabulünde `user_preferences.language` kaydını oluştur. -- [x] Invitation audit metadata'sına locale ekle. - -#### Portal shell ve sayfalar - -- [x] Portal sidebar -- [x] Portal hesap dropdown'ı ve çıkış -- [x] Portal dashboard -- [x] Portal projeler listesi -- [x] Portal proje detayı -- [x] Portal public görev listesi -- [x] Portal revizyon listesi -- [x] Revizyon oluşturma formu -- [x] Portal progress ve empty state'ler -- [x] Portal tarih, sayı ve durum label'ları - -#### İçerik çözümleme - -- [x] Proje ve planlama içeriklerini client locale'inde resolve et. -- [x] Public görevleri client locale'inde resolve et. -- [x] Eksik içerikte belirlenen fallback zincirini uygula. -- [x] Revizyon talebini yazıldığı dilde sakla. -- [x] Client başka locale'lerin edit setine erişemediğini test et. - -#### Yönetim - -- [x] Müşteri detayında mevcut portal dilini göster. -- [x] Portal dili değiştirme aksiyonu ekle. -- [x] Locale arşivleme öncesi etkilenen müşteri sayısını göster. -- [x] Client locale değişikliğinde aktif session'ın sonraki request'te yeni dili - kullanmasını sağla. - -#### Çıkış kriteri - -- [x] İngilizce seçilen davet linki İngilizce açılıyor. -- [x] Hesap kabulü sonrasında portal İngilizce kalıyor. -- [x] Fransızca client, Fransızca proje içeriğini görüyor. -- [x] Eksik Fransızca içerik güvenli fallback ile gösteriliyor. -- [x] Türkçe client davranışında regression yok. -- [x] Başka müşterinin locale veya çeviri verisi sızmıyor. - -#### Faz 6 çıktıları - -- [Faz 6 müşteri portal dili ve davet akışı raporu](i18n-phase-6.md) -- `server/auth/invitations.ts` -- `app/api/create-client-user/route.ts` -- `app/api/portal-invitations/route.ts` -- `app/api/portal-clients/[clientId]/locale/route.ts` -- `app/invite/[token]/page.tsx` -- `app/portal/layout.tsx` -- `components/layout/portal-shell.tsx` -- `config/portal-sidebar.ts` -- `app/portal/page.tsx` -- `app/portal/projects/page.tsx` -- `app/portal/projects/[id]/page.tsx` -- `app/portal/tasks/page.tsx` -- `app/portal/revisions/page.tsx` - -### Faz 7 — Auth, hata, bildirim ve erişilebilirlik bütünlüğü - -Amaç: Ana sayfalar dışında kalan uç metinleri ve locale edge case'lerini -tamamlamak. - -#### Görevler - -- [x] Login ekranında aktif diller arasında seçim sun. -- [x] İlk admin kurulumunda instance default locale davranışını tamamla. -- [x] Forgot/reset password ekranlarını kataloglaştır. -- [x] 404, error boundary ve maintenance metinlerini kataloglaştır. -- [x] Server action redirect query mesajlarını stabil hata kodlarına taşı. -- [x] Toast tekrarlarını engelleyen mevcut akışları locale değişiminde doğrula. -- [x] Erişilebilirlik label, `aria-label`, tooltip ve screen-reader metinlerini - kataloglaştır. -- [x] Interpolation ve plural örneklerini tüm built-in locale'lerde test et. -- [x] RTL smoke testi yap; desteklenmeyen layout noktalarını raporla ve düzelt. -- [x] Locale değiştirme kontrolünün focus ve klavye davranışını test et. -- [x] Bildirim/e-posta sistemi eklendiğinde kullanacağı locale sözleşmesini - belgeye bağla. - -#### Çıkış kriteri - -- [x] Auth öncesi ve sonrası locale geçişi tutarlı. -- [x] Kullanıcıya görünen server hata mesajları Türkçe'ye gömülü değil. -- [x] Erişilebilirlik metinleri de seçili locale'de. -- [x] RTL locale temel shell ve formları kullanılamaz hale getirmiyor. - -### Faz 8 — API v1 ve mobil hazırlık - -Amaç: Aynı dil modelini gelecekteki React Native istemcisi için stabil bir -sözleşmeye dönüştürmek. - -#### Görevler - -- [x] `instance.localization` capability kaydını ekle. -- [x] `/api/v1/meta` localization alanlarını ekle. -- [x] `/.well-known/neta` için gerekli additive locale bilgisini değerlendir. -- [x] `/api/v1/me` preference language ve portal locale alanlarını ekle. -- [x] `Accept-Language` parser ve locale negotiation helper'ını yaz. -- [x] Gelecek resource endpoint'leri için localized response contract'ı ekle. -- [x] Owner mutation contract'ında `translations` shape'ini standartlaştır. -- [x] `UNSUPPORTED_LOCALE` hata kodunu API response mapping'e ekle. -- [x] Client'ın unknown locale/capability değerlerini güvenli ele almasını - belgeye ekle. -- [x] Meta cache revalidation ve absolute URL davranışını test et. -- [x] API contract fixture'larını `tr`, `en`, `fr` için ekle. -- [x] Phase 9 mobile smoke testlerini localization alanlarıyla genişlet. - -#### Faz 8 çıktıları - -- [Faz 8 API v1 ve mobil hazırlık raporu](i18n-phase-8.md) -- `server/api/v1/localization.ts` -- `server/api/v1/contracts.ts` -- `scripts/i18n-phase8-smoke.mjs` -- `scripts/i18n-phase8-smoke.ts` -- `app/api/v1/me/route.ts` -- `scripts/phase9-api-boundary.mjs` -- `docs/self-hosted-redesign/i18n-phase-8-fixtures/` - -#### Çıkış kriteri - -- [x] Mobil istemci instance'ın desteklediği dilleri keşfedebiliyor. -- [x] `/me` kullanıcının çözülmüş dilini döndürüyor. -- [x] Eski v1 client'lar additive alanlar nedeniyle kırılmıyor. -- [x] Locale seçimi server authorization sınırını aşmıyor. - -### Faz 9 — Hardening, migrasyon ve release - -Amaç: Çok dilli özelliği self-host production kurulumu için güvenli şekilde -yayınlamak. - -#### Migrasyon - -- [x] Release öncesi otomatik backup zorunluluğunu belgeye ekle. -- [x] Boş DB migration testi yap. -- [x] Mevcut production benzeri Türkçe DB migration testi yap. -- [x] Tekrar çalışan idempotent backfill testi yap. -- [x] Migration failure sonrası eski sürüme dönüş prosedürünü test et. -- [x] Backup/restore sonrasında locale ve çeviri bütünlüğünü doğrula. -- [x] Supabase import aracının yeni locale default'larını doğru oluşturduğunu test +- Her resolver source bilgisini test ve gözlemlenebilirlik için döndürür. + +### 8.2. Preference ve admin default ayrımı + +- `saveLanguagePreference` yalnız oturum kullanıcısının preference'ını değiştirir. +- `setDefaultLocale` owner-only instance ayarıdır. +- `setClientPortalLocale` adminin client başlangıç/default dilini değiştirir. +- `setPortalUserLanguagePreference` portal kullanıcısının açık kişisel seçimini + değiştirir. +- Bu dört işlem aynı action adı veya aynı UI kontrolü altında karıştırılmaz. + +### 8.3. Sayfa bazlı katalog yükleme + +- Root/dashboard layout yalnız `common`, `navigation` ve shell için gereken + namespace'leri taşır. +- Her page kendi namespace'ini server tarafında yükler. +- Client component'e yalnız o sayfanın mesajları verilir. +- Custom translation değişiklikleri catalog version ile cache invalidate eder. + +### 8.4. API ve mobil uyumu + +Mobil istemci için locale yalnız web cookie'sine bağlı olmayacaktır. + +- `/api/v1/meta` aktif dilleri, default dili, direction ve catalog version'ı + döndürür. +- `/api/v1/me` kişisel tercih, client default ve resolved locale'i ayrı alanlarda + döndürür. +- Kullanıcı preference mutation endpoint'i hem freelancer hem client rolünü + güvenli biçimde destekler. +- Owner locale/language management endpoint'leri owner-only kalır. +- Domain mutation payload'u + `translations: Record>` şeklindedir. +- Domain response varsayılan olarak resolved içerik döndürür. +- Owner edit endpoint'i `translations` map'ini ayrıca döndürür. +- Portal actor yalnız resolved ve kendisine görünür içeriği alır. +- Web ile mobil aynı resolver/service katmanını kullanır. +- Locale tercihi URL/query/header ile geçici istenebilse bile authorization ve + aktif dil kontrollerini atlayamaz. + +## 9. Faz sırası ve uygulama kuralları + +- Her faz tek bir sayfa veya tek bir altyapı sorumluluğudur. +- Bir sayfanın UI çevirisi, form içerik çevirisi ve o sayfaya ait server + action/hata çevirileri aynı fazda tamamlanır. +- Bir fazın checklist'i ve kabul kriterleri tamamlanmadan sonraki sayfa fazına + geçilmez. +- Her faz sonunda master plan checkbox'ları gerçek sonuca göre işaretlenir. +- Her sayfa Türkçe, İngilizce ve eksik custom locale fixture'ıyla test edilir. +- Her faz `typecheck`, `lint`, ilgili smoke test ve `git diff --check` kapısından + geçer. +- Tasarım değişikliği gereken settings sayfaları Poyraz UI v3 bileşenleriyle + yapılır; hard-coded component renkleri eklenmez. + +## 10. Faz bazlı uygulama planı + +### Faz 0 — V2 baseline, envanter ve regression sözleşmesi + +Amaç: Eski implementasyonu ölçmek ve her route için tamamlanma tanımını +kilitlemek. + +- [ ] Auth ekranlarındaki locale select kullanımını test fixture'ıyla kaydet. +- [ ] Public, freelancer, portal ve invitation resolver davranışlarını ayrı test + et. +- [ ] Tüm route/page/client/action/loading/error dosyalarının kullanıcı metni + envanterini çıkar. +- [ ] Her sayfa için mevcut katalog key kapsamını ve hard-coded metin sayısını + raporla. +- [ ] Tüm domain create/edit formlarını ve metinsel alanları envanterle. +- [ ] Portalda görünen ve owner-only kalan alanları işaretle. +- [ ] V1 veri modeli için korunacak, düzeltilecek ve kaldırılacak parçaları ADR + olarak yaz. +- [ ] TR/EN/custom locale seed ve regression fixture'larını hazırla. +- [ ] Sayfa bazlı i18n smoke runner oluştur. +- [ ] Baseline build, typecheck ve lint sonuçlarını kaydet. + +Çıkış kriteri: + +- [ ] Hiçbir route veya kullanıcıya açık UI yüzeyi envanter dışında kalmadı. +- [ ] Her sonraki fazın ölçülebilir hard-coded-text baseline'ı var. + +### Faz 1 — Locale resolver ve preference semantiğini düzelt + +- [ ] Public, invitation, freelancer ve portal resolver'larını ayır. +- [ ] Public auth akışından cookie ve browser locale önceliğini kaldır. +- [ ] Freelancer için preference -> instance default zincirini uygula. +- [ ] Portal için preference -> client default -> instance default zincirini + uygula. +- [ ] Cookie'yi otorite değil yardımcı cache haline getir. +- [ ] Arşivlenmiş/eksik locale fallback davranışını tanımla. +- [ ] Preference ile client default değişikliklerinin birbirini ezmediğini test + et. +- [ ] `` ve `dir` değerinin doğru resolved locale'den geldiğini test et. -#### Otomasyon +Çıkış kriteri: -- [x] `phase-i18n:boundary` script'i ekle. -- [x] Hardcoded kullanıcı metni istisna listesini minimumda tut. -- [x] Built-in katalog parity ve interpolation değişken testlerini CI'a ekle. -- [x] Locale service authorization testlerini CI'a ekle. -- [x] Content translation transaction testlerini CI'a ekle. -- [x] Portal davet locale E2E testini CI'a ekle. -- [x] Build, typecheck, lint ve mevcut phase smoke testlerini çalıştır. -- [x] Docker standalone image içinde built-in katalogların bulunduğunu doğrula. +- [ ] Dört bağlamın resolver testleri bağımsız geçiyor. +- [ ] Login sayfası eski locale cookie'sinden etkilenmiyor. -#### Performans ve gözlem +### Faz 2 — Settings route iskeleti ve kalıcı iç sidebar -- [x] Freelancer dashboard render süresini önce/sonra ölç. -- [x] Portal proje listesi query sayısını önce/sonra ölç. -- [x] Custom katalog yükleme boyutunu ölç. -- [x] Missing translation sayacını logla; hassas içerik loglama. -- [x] Catalog version/cache invalidation yarış koşullarını test et. +- [ ] `/settings` route'unu `/settings/general` sayfasına yönlendir. +- [ ] Settings layout ve sticky iç sidebar oluştur. +- [ ] Desktop ve mobil settings navigasyonunu uygula. +- [ ] Aktif route, loading, not-found ve error sınırlarını ekle. +- [ ] Owner-only menü öğelerini yetkiye göre sınırla. +- [ ] Tek sayfalık client-state tab yapısını kaldırmaya hazır action sınırlarını + ayır. +- [ ] Settings shell'in TR/EN kataloglarını tamamla. -#### Dokümantasyon +Çıkış kriteri: -- [x] Self-host kurulum dokümanına default locale ayarını ekle. -- [x] Dil ekleme ve çeviri import/export rehberi yaz. -- [x] Portal müşterisine dil atama rehberi yaz. -- [x] Mobil API localization sözleşmesini güncelle. -- [x] Backup/restore dokümanına translation tablolarını ekle. -- [x] Release note ve upgrade uyarısını yaz. +- [ ] Refresh ve deep-link her settings alt route'unda çalışıyor. +- [ ] İç sidebar uzun sayfada sabit kalıyor. -#### Çıkış kriteri +### Faz 3 — Ayarlar / Genel sayfası -- [x] Türkçe ve İngilizce kataloglar yüzde 100 tamamlandı. -- [x] Fransızca custom locale uçtan uca smoke testi geçti. -- [x] Davet öncesi, davet, login ve portal locale zinciri doğrulandı. -- [x] Mevcut Türkçe veri kaybı veya görünüm regression'ı yok. -- [x] Docker/Dokploy persistent volume backup-restore testi geçti. -- [x] Mobil v1 sözleşmesi geriye uyumlu. -- [x] Release readiness raporu yazıldı. +Route: `/settings/general` -## 16. Test matrisi +- [ ] Workspace adı, meta title, short name ve genel instance alanlarını taşı. +- [ ] Portal welcome/footer için aktif dil tab'larını uygula. +- [ ] Branding create/update action'ını route'a özel hale getir. +- [ ] Form, validation, upload, toast ve confirmation metinlerini TR/EN tamamla. +- [ ] Metadata ve favicon revalidation davranışını koru. +- [ ] Owner authorization ve dosya cleanup testlerini koru. -| Senaryo | Beklenen sonuç | -| --- | --- | -| Yeni kurulum | `tr` ve `en` aktif, default `tr` | -| Mevcut DB upgrade | Eski içerik Türkçe translation olarak backfill | -| Owner dili İngilizce | Freelancer UI ve formatlar İngilizce | -| Eksik Fransızca UI key | Tanımlı fallback metni, ham key değil | -| Fransızca proje içeriği | Fransızca client Fransızca metni görür | -| Eksik Fransızca proje alanı | Default içerik fallback'i gösterilir | -| İngilizce portal daveti | Davet kabul sayfası İngilizce | -| Davet kabulü | Client preference dili invitation locale olur | -| Client tekrar login | Portal dili korunur | -| Locale arşivleme | Referanslı client varsa yönlendirme istenir | -| Translation import | Preview sonrası geçerli key'ler yazılır | -| Zararlı import key'i | Validation ile reddedilir | -| Client translation endpoint'i | 403/404 ile reddedilir | -| Başka client projesi | Locale değişse de erişilemez | -| RTL test locale'i | `dir=rtl`, shell ve form kullanılabilir | -| Backup/restore | Locale, overrides ve içerik çevirileri korunur | -| Eski mobil client | Yeni additive meta alanlarını yok sayarak çalışır | +Çıkış kriteri: -## 17. Riskler ve azaltma yaklaşımı +- [ ] Genel sayfasında hard-coded kullanıcı metni kalmadı. +- [ ] Branding içeriklerinin tüm aktif dil değerleri kalıcı. -### 17.1. Hardcoded metin kaçakları +### Faz 4 — Ayarlar / Görünüm sayfası -Risk: Bazı toast, tooltip veya empty state metinleri Türkçe kalabilir. +Route: `/settings/appearance` -Azaltma: +- [ ] Light/dark/system kişisel tercihini taşı. +- [ ] Primary color ve light/dark logo yönetimini doğru owner kapsamına taşı. +- [ ] Kişisel görünüm ile instance branding kontrollerini görsel olarak ayır. +- [ ] Preview, upload, reset ve validation metinlerini çevir. +- [ ] Dark/light ve mobil davranışı test et. -- Namespace bazlı sayfa checklist'i -- Boundary script -- Türkçe karakter/string taraması -- İngilizce E2E ekran gezintisi +Çıkış kriteri: -### 17.2. Client/server locale uyuşmazlığı +- [ ] Görünüm sayfası TR/EN eksiksiz ve tema geçişinde hydration hatasız. -Risk: Server Türkçe, client İngilizce render ederek hydration hatası oluşturabilir. +### Faz 5 — Ayarlar / Profil sayfası -Azaltma: +Route: `/settings/profile` -- Locale'i layout server tarafında bir kez çözmek -- Client provider'a resolved locale ve namespace snapshot vermek -- İlk render öncesi browser dilini bağımsız yeniden seçmemek +- [ ] Ad, soyad ve avatar formunu taşı. +- [ ] Account bilgisi, upload ve toast metinlerini çevir. +- [ ] Kişisel profile action'ını owner-only instance action'lardan ayır. +- [ ] Validation ve hata kodlarını locale-aware hale getir. -### 17.3. Translation sorgularında N+1 +Çıkış kriteri: -Risk: Liste ekranlarında her proje için ayrı query çalışabilir. +- [ ] Profil sayfasında tüm kullanıcı metinleri resolved locale ile geliyor. -Azaltma: +### Faz 6 — Ayarlar / Güvenlik sayfası -- Batch resolver -- Composite index -- Query-count smoke testi +Route: `/settings/security` -### 17.4. Eksik müşteri çevirisi +- [ ] Şifre değiştirme formunu taşı. +- [ ] Mevcut/yeni şifre validation ve hata mesajlarını çevir. +- [ ] Session revoke davranışını açık ve erişilebilir biçimde göster. +- [ ] Success/error toast'larını tek kaynak üzerinden üret. -Risk: Müşteri portalı kısmen farklı dillerde görünebilir. +Çıkış kriteri: -Azaltma: +- [ ] Güvenlik akışı TR/EN ve custom fallback locale ile çalışıyor. -- Portal kritik namespace kapsamı -- Publish readiness yüzdesi -- Davet dropdown'unda yalnız portal-ready locale -- İçerik alanlarında eksik tab uyarısı -- Belirgin ve deterministik fallback +### Faz 7 — Ayarlar / Yapay zekâ sayfası -### 17.5. Eski kolon ve translation tablosu ayrışması +Route: `/settings/ai` -Risk: Bir write yalnız tablolardan birini günceller. +- [ ] Provider, model ve API key kontrollerini taşı. +- [ ] Secret mask, mevcut key, değiştirme ve hata UX'ini düzenle. +- [ ] AI provider ve bağlantı hata metinlerini çevir. +- [ ] Owner-only authorization'ı doğrula. -Azaltma: +Çıkış kriteri: -- Tek service mutation noktası -- Transaction -- Repository'ye doğrudan UI erişimini boundary testiyle engelleme -- Tutarlılık kontrol script'i +- [ ] AI ayarlarında sabit Türkçe/İngilizce metin yok. -### 17.6. Custom çeviride XSS veya bozuk interpolation +### Faz 8 — Ayarlar / Kişisel dil tercihi sayfası -Risk: Owner'ın girdiği metin HTML veya sahte placeholder ile render'ı bozabilir. +Route: `/settings/language` -Azaltma: +- [ ] Yalnız aktif dilleri göster. +- [ ] Kişisel tercih ile instance default bilgisini ayrı göster. +- [ ] Preference mutation'ı uygula ve layout'u güvenli yenile. +- [ ] Arşivlenmiş preference için fallback ve yeniden seçim uyarısı ekle. +- [ ] Dil adlarını native name + locale code ile göster. -- Düz metin varsayımı -- Placeholder schema doğrulaması -- HTML render etmeme -- Import limitleri ve güvenli key parser +Çıkış kriteri: -## 18. Önerilen commit parçalama stratejisi +- [ ] Owner dili yalnız bu sayfadan değiştirilebiliyor. +- [ ] Login/auth ekranlarında dil seçici bulunmuyor. -Her faz kendi içinde rollback edilebilir küçük commit'lere ayrılmalıdır: +### Faz 9 — Ayarlar / Diller liste sayfası -1. Şema, migration ve repository -2. Service, validation ve test -3. Ortak UI/runtime altyapısı -4. Sayfa/form entegrasyonları -5. Dokümantasyon ve phase checklist güncellemesi +Route: `/settings/languages` -Şema migration'ı ile onu kullanan runtime kodu ayrı deploy'larda uyumsuz hale -gelmemelidir. Gerekirse expand/migrate/contract sırası izlenir. +- [ ] Yeni dil yönetim listesi UX'ini uygula. +- [ ] Default, status, fallback, completion ve usage sütunlarını ekle. +- [ ] Instance default locale kontrolünü bu sayfaya taşı. +- [ ] Draft/active/archived filtrelerini ekle. +- [ ] Loading, empty, error ve permission state'lerini çevir. -## 19. Genel tamamlanma checklist'i +Çıkış kriteri: -- [x] Faz 0 — Envanter, ADR ve fixture -- [x] Faz 1 — Locale veri modeli ve service -- [x] Faz 2 — Runtime ve built-in kataloglar -- [x] Faz 3 — Dil/çeviri yönetimi -- [x] Faz 4 — Freelancer UI migration -- [x] Faz 5 — Çok dilli içerik ve form tab'ları -- [x] Faz 6 — Portal dili ve davet -- [x] Faz 7 — Auth/hata/a11y bütünlüğü -- [x] Faz 8 — API ve mobil hazırlık -- [x] Faz 9 — Hardening ve release +- [ ] Dil listesi çeviri editörüyle aynı ekranda değil. +- [ ] Default dil ve kişisel dil tercihi birbirine karışmıyor. -## 20. Nihai definition of done +### Faz 10 — Ayarlar / Yeni dil sayfası -Bu özellik aşağıdaki maddelerin tamamı sağlanmadan bitmiş sayılmaz: +Route: `/settings/languages/new` -- [ ] Türkçe ve İngilizce yeni kurulumda hazır gelir. -- [ ] Owner Ayarlar'dan üçüncü bir dil ekleyebilir. -- [ ] Owner custom dilde bütün sistem metinlerini düzenleyebilir. -- [ ] Sidebar, başlıklar, butonlar, formlar, durumlar ve hatalar seçili dilde - gösterilir. -- [ ] Tarih, saat, sayı ve para locale'e göre biçimlenir. -- [ ] Çevrilebilir formlarda aktif diller tab olarak görünür. -- [ ] Proje ve müşteriye açık görev içerikleri dil başına saklanır. -- [ ] Portal davetinde müşteri dili seçilir. -- [ ] Davet kabul ekranı seçilen dilde açılır. -- [ ] Müşteri her login sonrasında portalı atanmış dilde görür. -- [ ] Portal içerikleri müşteri locale'ine göre çözülür. -- [ ] Eksik çeviri davranışı deterministik ve güvenlidir. -- [ ] Client authorization locale parametresiyle aşılamaz. -- [ ] Mobil metadata desteklenen dilleri ilan eder. -- [ ] Backup/restore bütün custom dilleri ve çevirileri korur. -- [ ] Mevcut Türkçe veriler migration sırasında kaybolmaz. -- [ ] Build, typecheck, lint, i18n boundary ve E2E smoke testleri geçer. +- [ ] BCP 47 locale formunu uygula. +- [ ] Native ad, yönetim adı, direction ve fallback alanlarını ekle. +- [ ] Custom dilin daima draft oluşmasını sağla. +- [ ] Duplicate, self-fallback ve fallback-loop hatalarını çevir. +- [ ] Başarılı kayıtta dil detayına yönlendir. + +Çıkış kriteri: + +- [ ] Geçersiz locale DB'ye ulaşmadan reddediliyor. + +### Faz 11 — Ayarlar / Dil detay sayfası + +Route: `/settings/languages/[locale]` + +- [ ] Metadata ve lifecycle yönetimini uygula. +- [ ] Namespace/sayfa bazlı completion özetini ekle. +- [ ] Kullanıcı/client kullanım etkisini göster. +- [ ] Activate/default/archive readiness kontrollerini uygula. +- [ ] Built-in korumalarını UI ve service katmanında test et. + +Çıkış kriteri: + +- [ ] Eksik kritik çevirili draft dil aktif edilemiyor. + +### Faz 12 — Ayarlar / Çeviri editörü + +Route: `/settings/languages/[locale]/translations` + +- [ ] Sayfa/modül bazlı navigation ve filtreleri uygula. +- [ ] TR/EN kaynak, fallback preview ve hedef input'ları ayrıştır. +- [ ] Dirty state, toplu kaydetme ve çıkış uyarısı ekle. +- [ ] Eksik/değişmiş/portal kritik filtrelerini uygula. +- [ ] Interpolation ve maksimum uzunluk doğrulamasını göster. +- [ ] Reset override aksiyonunu güvenli hale getir. +- [ ] Büyük katalogda pagination/virtualization performansını ölç. + +Çıkış kriteri: + +- [ ] Teknik key bilmeyen owner çevirileri sayfa bazında tamamlayabiliyor. + +### Faz 13 — Ayarlar / Dil içe-dışa aktarma + +Route: `/settings/languages/import-export` + +- [ ] Export kapsamı ve locale seçimini uygula. +- [ ] Dosya upload, schema validation ve preview ekranı ekle. +- [ ] Create/update/skip/conflict özetini göster. +- [ ] Overwrite için açık confirmation iste. +- [ ] Boyut, key allowlist, prototype pollution ve interpolation kontrollerini + uygula. + +Çıkış kriteri: + +- [ ] Import işlemi preview olmadan mutation yapmıyor. + +### Faz 14 — Login sayfası + +Route: `/login` + +- [ ] Locale select component'ini kaldır. +- [ ] Sayfayı yalnız public instance locale ile render et. +- [ ] Form, marketing alanı, action error, toast ve accessibility metinlerini + tamamla. +- [ ] Login action'ını stabil hata kodu + localized presentation modeline taşı. +- [ ] Eski locale cookie ile regression testi ekle. + +Çıkış kriteri: + +- [ ] Login ekranında hiçbir dil değiştirme kontrolü yok. +- [ ] Login TR/EN katalog parity yüzde 100. + +### Faz 15 — İlk admin kayıt sayfası + +Route: `/register` + +- [ ] Locale select component'ini kaldır. +- [ ] Sayfayı yalnız public instance locale ile render et. +- [ ] İlk kurulum, kapalı kayıt, form ve action hata metinlerini tamamla. +- [ ] Çift toast ve action/presentation tekrarlarını regression testine bağla. + +Çıkış kriteri: + +- [ ] Register ekranında dil seçici ve hard-coded kullanıcı metni yok. + +### Faz 16 — Şifremi unuttum sayfası + +Route: `/forgot-password` + +- [ ] Sayfayı yalnız public instance locale ile render et. +- [ ] Form, provider durumu, success/error ve geri dönüş metinlerini çevir. +- [ ] Locale cookie'den etkilenmediğini test et. + +Çıkış kriteri: + +- [ ] Forgot-password TR/EN ve instance default davranışı tamamlandı. + +### Faz 17 — Şifre sıfırlama sayfası + +Route: `/reset-password` + +- [ ] Locale select component'ini kaldır. +- [ ] Sayfayı yalnız public instance locale ile render et. +- [ ] Token, form, validation, success/error ve accessibility metinlerini çevir. +- [ ] Geçersiz/expired token state'lerini test et. + +Çıkış kriteri: + +- [ ] Reset-password ekranında dil seçici ve sabit dil metni yok. + +### Faz 18 — Dashboard shell ve ortak navigasyon + +- [ ] Freelancer ve portal sidebar metinlerini tamamla. +- [ ] Account dropdown, logout, tooltip, mobile menu ve progress metinlerini + tamamla. +- [ ] Default Türkçe fallback label objelerini kaldır veya yalnız güvenli + developer fallback'e dönüştür. +- [ ] Root layout'a tüm namespace'leri göndermeyi bırak. +- [ ] Settings bağlantılarını yeni route'lara bağla. + +Çıkış kriteri: + +- [ ] Shell'de hard-coded kullanıcı metni ve raw key yok. + +### Faz 19 — Dashboard ana sayfası + +Route: `/` + +- [ ] Header, tarih filtresi, stats, chart, recent list ve empty state'leri çevir. +- [ ] Tarih, para, sayı, mood ve yüzde formatlarını locale-aware yap. +- [ ] Loading/error bileşenlerini tamamla. +- [ ] Dashboard client component hard-coded metin taramasını sıfırla. + +Çıkış kriteri: + +- [ ] Dashboard TR ve EN'de görsel/metinsel olarak eksiksiz. + +### Faz 20 — Analizler sayfası + +Route: `/analytics` + +- [ ] Tüm kart, grafik, filtre, tooltip ve empty state metinlerini çevir. +- [ ] Sayı, tarih ve para formatlarını locale-aware yap. +- [ ] Loading state'i çevir. + +Çıkış kriteri: + +- [ ] Analiz sayfasında sabit kullanıcı metni kalmadı. + +### Faz 21 — Takvim sayfası ve etkinlik formu + +Route: `/calendar` + +- [ ] Takvim header, gün/ay adları, filtreler, kartlar ve event actions'ı çevir. +- [ ] Etkinlik create/edit formunda title/description için aktif dil tab'ları + ekle. +- [ ] Type label, validation, toast ve confirmation metinlerini çevir. +- [ ] Calendar event translation CRUD ve backfill'i tamamla. + +Çıkış kriteri: + +- [ ] Etkinlik metinleri tüm aktif dillerde ayrı saklanıyor. + +### Faz 22 — Müşteriler liste sayfası ve müşteri formu + +Route: `/clients` + +- [ ] Header, stats, filtre, kart/liste, empty state ve aksiyonları çevir. +- [ ] Müşteri notes alanı için aktif dil tab'ları ekle. +- [ ] Kimlik alanlarını tab dışında tekil bırak. +- [ ] Tarih ve durum label'larını locale-aware yap. + +Çıkış kriteri: + +- [ ] Müşteri listesi ve create/edit formu TR/EN eksiksiz. + +### Faz 23 — Müşteri detay sayfası + +Route: `/clients/[id]` + +- [ ] Detail header, stats, tabs, portal account dialog ve activity alanlarını + çevir. +- [ ] Client activity title/content alanlarına dil tab'ları ekle. +- [ ] Portal başlangıç dilini yalnız aktif dillerden seçtir. +- [ ] Admin portal default'u ile müşterinin kişisel tercihini açıklayan UX ekle. +- [ ] Davet, resend, revoke ve locale update hata/toast'larını çevir. + +Çıkış kriteri: + +- [ ] Portal hesabı açılırken admin tarafından başlangıç dili belirleniyor. + +### Faz 24 — Projeler liste sayfası ve proje formu + +Route: `/projects` + +- [ ] Header, stats, filtre, grid/list, risk analizi ve empty state'leri çevir. +- [ ] Project name/description/coverImageAlt dil tab'larını eksiksiz uygula. +- [ ] Create/edit action ve validation mesajlarını çevir. +- [ ] Liste okumalarını locale-resolved ve batch hale getir. + +Çıkış kriteri: + +- [ ] Proje formu tüm aktif dilleri kayıpsız düzenliyor. + +### Faz 25 — Proje detay sayfası + +Route: `/projects/[id]` + +- [ ] Header, stats, tabs, progress, revisions, files ve actions metinlerini + çevir. +- [ ] Planning section title/content dil tab'larını tamamla. +- [ ] Detail içindeki task formunun aynı translation sözleşmesini kullandığını + doğrula. +- [ ] Portal preview/fallback bilgisini görünür kıl. +- [ ] Loading/error state'lerini çevir. + +Çıkış kriteri: + +- [ ] Proje detayındaki bütün alt yüzeyler TR/EN tamamlandı. + +### Faz 26 — Görevler sayfası ve görev formu + +Route: `/tasks` + +- [ ] Header, stats, kanban/list, filtre, priority/status ve actions'ı çevir. +- [ ] Task title/description aktif dil tab'larını tamamla. +- [ ] Create/edit/delete action error ve toast'larını çevir. +- [ ] Public-to-client görevlerde hedef locale eksikliği uyarısını ekle. + +Çıkış kriteri: + +- [ ] Kanban ve liste görünümlerinde sabit metin kalmadı. + +### Faz 27 — Finans sayfası ve işlem formu + +Route: `/finance` + +- [ ] Header, stats slider, filtre, tablo/kart, AI modal ve empty state'i çevir. +- [ ] Finance category/description alanlarına aktif dil tab'ları ekle. +- [ ] Tutar, currency, vergi, ödeme durumu ve tarihleri locale-aware göster. +- [ ] Create/edit/delete ve AI action hata metinlerini çevir. +- [ ] Finance translation registry, backfill ve batch read ekle. + +Çıkış kriteri: + +- [ ] Finans formu eklenen tüm aktif diller için ayrı metin saklıyor. + +### Faz 28 — Günlük sayfası ve günlük formu + +Route: `/journal` + +- [ ] Header, mood/energy alanları, list, empty state ve actions'ı çevir. +- [ ] Mood label ve note alanlarına aktif dil tab'ları ekle. +- [ ] Skorlar ve tarih alanlarını ortak tut. +- [ ] AI-derived içerikte source locale davranışını belirginleştir. + +Çıkış kriteri: + +- [ ] Günlük formundaki çevrilebilir alanlar locale bazlı kalıcı. + +### Faz 29 — Sohbet sayfası + +Route: `/chat` + +- [ ] Sidebar, yeni sohbet, input, empty state, suggestions ve error metinlerini + çevir. +- [ ] API hata kodlarını ayrıntılı ve locale-aware sunuma bağla. +- [ ] Kullanıcı/assistant mesajlarını çeviri tab'ına sokma; source locale + metadata'sını koru. +- [ ] Session title düzenlenebiliyorsa locale modelini uygula. + +Çıkış kriteri: + +- [ ] Chat UI çevriliyor, mesajların orijinal dili bozulmuyor. + +### Faz 30 — Teklifler sayfası ve formu + +Route: `/business/proposals` + +- [ ] Liste, form, status, empty state ve actions'ı çevir. +- [ ] Proposal title/description aktif dil tab'larını ekle. +- [ ] Tutar/currency/status alanlarını ortak tut. +- [ ] Translation CRUD ve backfill ekle. + +Çıkış kriteri: + +- [ ] Teklif içeriği tüm aktif dillerde düzenlenebiliyor. + +### Faz 31 — Faturalar sayfası ve formu + +Route: `/business/invoices` + +- [ ] Liste, form, status, tarih, tutar ve actions'ı çevir. +- [ ] Mevcut şemada çevrilebilir serbest metin alanı olmadığını doğrula. +- [ ] İleride not/açıklama eklenirse registry sözleşmesini dokümante et. +- [ ] Tarih/para formatlarını locale-aware yap. + +Çıkış kriteri: + +- [ ] Fatura sayfasının bütün sistem metinleri TR/EN tamamlandı. + +### Faz 32 — Abonelikler sayfası ve formu + +Route: `/business/subscriptions` + +- [ ] Liste, form, billing cycle, status ve actions'ı çevir. +- [ ] Subscription name/category alanlarına aktif dil tab'ları ekle. +- [ ] Tutar, currency ve tarih alanlarını ortak tut. +- [ ] Translation CRUD ve backfill ekle. + +Çıkış kriteri: + +- [ ] Abonelik metinleri locale bazlı saklanıyor. + +### Faz 33 — Portal ayarlar layout'u + +Route: `/portal/settings` + +- [ ] Eksik portal settings route ve layout'unu oluştur. +- [ ] Portal için mobil/desktop settings nav ekle. +- [ ] Language, appearance, profile ve security alt route'larını tanımla. +- [ ] Client'ın owner-only ayarlara erişemediğini test et. +- [ ] Portal settings shell metinlerini tamamla. + +Çıkış kriteri: + +- [ ] Sidebar'daki portal settings bağlantısı geçerli bir sayfaya gidiyor. + +### Faz 34 — Portal dil tercihi sayfası + +Route: `/portal/settings/language` + +- [ ] Admin tarafından atanmış başlangıç dilini bilgi olarak göster. +- [ ] Yalnız aktif instance dillerini seçim olarak sun. +- [ ] Client personal preference mutation'ını uygula. +- [ ] “Kişisel seçimi kaldır / admin varsayılanını kullan” davranışını tasarla. +- [ ] Arşivlenmiş dil fallback ve uyarısını uygula. + +Çıkış kriteri: + +- [ ] Portal kullanıcısı yalnız adminin aktif ettiği diller arasında geçiş + yapabiliyor. + +### Faz 35 — Portal görünüm sayfası + +Route: `/portal/settings/appearance` + +- [ ] Kişisel light/dark/system tema seçimini uygula. +- [ ] Tema preference action'ını portal actor için yetkilendir. +- [ ] Preview, seçenek, success/error ve accessibility metinlerini çevir. +- [ ] Instance branding kontrollerinin client'a açılmadığını test et. + +Çıkış kriteri: + +- [ ] Portal görünüm tercihi owner instance ayarlarından izole. + +### Faz 36 — Portal profil sayfası + +Route: `/portal/settings/profile` + +- [ ] Client ad, soyad ve avatar formunu uygula. +- [ ] Profile action'ını yalnız oturumdaki portal kullanıcısına sınırla. +- [ ] Upload, validation, success/error ve accessibility metinlerini çevir. + +Çıkış kriteri: + +- [ ] Portal profil sayfası TR/EN ve custom fallback ile çalışıyor. + +### Faz 37 — Portal güvenlik sayfası + +Route: `/portal/settings/security` + +- [ ] Client şifre değiştirme ve session kontrollerini uygula. +- [ ] Security action'larını portal actor için yetkilendir. +- [ ] Validation, success/error ve session revoke metinlerini çevir. + +Çıkış kriteri: + +- [ ] Portal güvenlik akışı owner ayarlarından izole ve locale-aware. + +### Faz 38 — Portal davet sayfası + +Route: `/invite/[token]` + +- [ ] Dil seçici olmadan invitation snapshot locale'i kullan. +- [ ] Expired/accepted/revoked/success state'lerini eksiksiz çevir. +- [ ] Davet kabulünde client preference başlangıç değerini atomik yaz. +- [ ] Davet locale'i geçersiz/arşivlenmişse kontrollü fallback uygula. + +Çıkış kriteri: + +- [ ] Davet sayfası adminin belirlediği dilde açılıyor. + +### Faz 39 — Portal dashboard + +Route: `/portal` + +- [ ] Header, stats, proje kartları ve empty state'i çevir. +- [ ] Branding welcome/footer içeriğini resolved locale ile göster. +- [ ] Tarih, sayı ve progress formatlarını locale-aware yap. +- [ ] Project translation batch read'i doğrula. + +Çıkış kriteri: + +- [ ] Portal dashboard client preference değişince tamamen dil değiştiriyor. + +### Faz 40 — Portal projeler listesi + +Route: `/portal/projects` + +- [ ] Header, filtre/kart, status ve empty state metinlerini çevir. +- [ ] Project name/description/alt değerlerini resolved locale ile göster. +- [ ] Fallback zinciri ve batch query performansını test et. + +Çıkış kriteri: + +- [ ] Client yalnız resolved proje içeriğini alıyor. + +### Faz 41 — Portal proje detay sayfası + +Route: `/portal/projects/[id]` + +- [ ] Overview, plan, tasks, revisions, progress ve dialog metinlerini çevir. +- [ ] Project, planning section ve public task içeriklerini resolved locale ile + göster. +- [ ] Revision mesajını source locale ile sakla. +- [ ] Error, permission ve empty state'leri tamamla. + +Çıkış kriteri: + +- [ ] Proje detayının bütün alt tab'ları seçili portal dilinde. + +### Faz 42 — Portal görevler sayfası + +Route: `/portal/tasks` + +- [ ] Header, kart/list, status, tarih ve empty state'i çevir. +- [ ] Task title/description değerlerini resolved locale ile göster. +- [ ] Client scope ve fallback davranışını test et. + +Çıkış kriteri: + +- [ ] Portal görevlerinde raw default-locale metni sızmıyor. + +### Faz 43 — Portal revizyonlar sayfası + +Route: `/portal/revisions` + +- [ ] Header, status, kartlar, tarih ve empty state'i çevir. +- [ ] Kullanıcının yazdığı revision description'ı orijinal dilde göster. +- [ ] Source locale bilgisini sakla ve API contract'a ekle. + +Çıkış kriteri: + +- [ ] Sistem metni çevriliyor, kullanıcı mesajı değiştirilmeden kalıyor. + +### Faz 44 — Ortak feedback, status ve edge sayfaları + +- [ ] `not-found`, root error, route loading ve maintenance ekranlarını denetle. +- [ ] FeedbackState, StatusBadge, confirmation, toaster ve ortak form + component'lerini çevir. +- [ ] Bütün enum label'larını merkezi status kataloglarına taşı. +- [ ] Default hard-coded Türkçe label'ları kaldır. +- [ ] Accessibility ve metadata metinlerini tamamla. + +Çıkış kriteri: + +- [ ] Ortak component'ten hiçbir sayfaya sabit dil metni sızmıyor. + +### Faz 45 — API ve mobil localization sözleşmesi + +- [ ] Meta ve me response'larında default, preference, client default ve resolved + locale alanlarını ayrıştır. +- [ ] Freelancer/client preference mutation endpoint'lerini tamamla. +- [ ] Owner language management endpoint'lerini belge ve test et. +- [ ] Domain translations mutation/read sözleşmesini bütün entity'lere uygula. +- [ ] Custom locale katalog indirme/version endpoint'ini tamamla. +- [ ] `Accept-Language` ve açık locale isteğinin güvenli sınırlarını test et. +- [ ] OpenAPI/contract fixture'larını TR, EN ve custom locale için güncelle. + +Çıkış kriteri: + +- [ ] Web dışındaki bir istemci cookie kullanmadan aynı locale davranışını + uygulayabiliyor. + +### Faz 46 — Veri migrasyonu ve backfill + +- [ ] Yeni entity type/field registry için Drizzle migration üret. +- [ ] Mevcut verileri instance default locale'e idempotent backfill et. +- [ ] Preference/client/invitation locale tutarsızlıklarını raporlayan script + ekle. +- [ ] Orphan translation cleanup ve integrity kontrolü ekle. +- [ ] Backup, dry-run, rollback ve restore prosedürlerini dokümante et. +- [ ] Büyük fixture üzerinde migration süresini ölç. + +Çıkış kriteri: + +- [ ] Mevcut self-host verisi kayıpsız şekilde yeni modele taşınıyor. + +### Faz 47 — Release hardening ve son kabul + +- [ ] Her route'u TR ve EN ile browser smoke testinden geçir. +- [ ] Custom draft/active/archived locale senaryolarını test et. +- [ ] Login ekranında dil seçici olmadığını regression testine bağla. +- [ ] Owner ve portal preference ayrımını uçtan uca test et. +- [ ] Tüm create/edit formlarında aktif dil tab'larını kontrol et. +- [ ] Hard-coded user-facing text taramasını release gate yap. +- [ ] Katalog parity, boş değer ve interpolation testlerini release gate yap. +- [ ] RTL layout smoke, accessibility ve keyboard navigation testlerini çalıştır. +- [ ] Translation liste okumalarında N+1 ve payload boyutunu ölç. +- [ ] Typecheck, lint, unit, integration, browser ve production build'i çalıştır. +- [ ] Self-host upgrade ve yeni kurulum dokümantasyonunu güncelle. + +Çıkış kriteri: + +- [ ] Türkçe ve İngilizce bütün sayfalarda eksiksiz. +- [ ] Custom dil kod değişikliği olmadan eklenip aktif edilebiliyor. +- [ ] Owner ve portal kullanıcısı dili yalnız kendi ayar ekranından + değiştirebiliyor. +- [ ] Her çevrilebilir domain formu aktif dil sayısı kadar tab gösteriyor. +- [ ] Portal başlangıç dili admin tarafından belirleniyor ve client tarafından + izinli diller içinde değiştirilebiliyor. +- [ ] Release pipeline tüm i18n kalite kapılarında yeşil. + +## 11. Global kabul matrisi + +Her sayfa fazında aşağıdaki matris doldurulmadan checkbox'lar tamamlanmış +sayılmaz: + +| Kontrol | TR | EN | Custom/fallback | +| --- | --- | --- | --- | +| Page ve metadata | Bekliyor | Bekliyor | Bekliyor | +| Form ve validation | Bekliyor | Bekliyor | Bekliyor | +| Dialog/toast/error | Bekliyor | Bekliyor | Bekliyor | +| Empty/loading state | Bekliyor | Bekliyor | Bekliyor | +| Tarih/sayı/para | Bekliyor | Bekliyor | Bekliyor | +| Keyboard/a11y | Bekliyor | Bekliyor | Bekliyor | +| Hard-coded text taraması | Bekliyor | Bekliyor | Bekliyor | + +## 12. Plan dışı konular + +Bu plan aşağıdakileri otomatik olarak kapsamaz: + +- Makine çevirisi sağlayıcısı +- Chat/revision mesajlarını otomatik çevirme +- Locale içeren URL yapısı (`/tr/...`) +- Profesyonel çevirmen workflow'u ve review rolleri +- Kullanıcı başına timezone/currency yeniden tasarımı +- Built-in legacy metin kolonlarını tamamen kaldırma + +Bunlar V2 tamamlandıktan sonra ayrı ürün kararları olarak planlanabilir. diff --git a/docs/self-hosted-redesign/neta-multilingual-i18n-v1-legacy-plan.md b/docs/self-hosted-redesign/neta-multilingual-i18n-v1-legacy-plan.md new file mode 100644 index 0000000..5c0e1eb --- /dev/null +++ b/docs/self-hosted-redesign/neta-multilingual-i18n-v1-legacy-plan.md @@ -0,0 +1,1337 @@ +--- +title: Neta Çok Dilli Sistem ve Yerelleştirme Ana Planı +description: Self-host edilen Neta instance'larında yönetilebilir arayüz dilleri, çok dilli içerik formları, müşteri portalı dili ve mobil API uyumu için faz bazlı uygulama planı. +status: completed +current_phase: "completed" +last_updated: 2026-07-19 +--- + +# Neta Çok Dilli Sistem ve Yerelleştirme Ana Planı + +## 1. Belgenin amacı + +Bu plan, Neta'yı yalnızca Türkçe metinler gösteren bir uygulamadan, self-host eden +kişinin yönetebildiği çok dilli bir platforma dönüştürür. + +Hedef sistemde: + +- Türkçe (`tr`) ve İngilizce (`en`) ilk kurulumda hazır ve aktif gelir. +- Instance sahibi Ayarlar sayfasından yeni bir dil ekleyebilir. +- Eklenen dil için sidebar, sayfa başlıkları, butonlar, durumlar, formlar, + doğrulama mesajları, auth ekranları ve müşteri portalı metinleri çevrilebilir. +- Çevrilebilir iş içerikleri, aynı form içinde dil sekmeleriyle girilebilir. +- Müşteri portal daveti oluşturulurken müşterinin dili seçilir. +- Davet ekranı dahil olmak üzere müşteri portalı seçilen dilde açılır. +- Tarih, saat, sayı ve para gösterimleri seçili locale'e göre biçimlendirilir. +- Web ile aynı dil ve içerik modeli gelecekteki React Native istemcisi tarafından + API üzerinden kullanılabilir. +- Yeni bir dil eklemek yeni bir deployment veya kod değişikliği gerektirmez. + +Bu belge implementasyon sırasını, veri modelini, fallback davranışını, UI +sözleşmelerini, migrasyon stratejisini, güvenlik sınırlarını ve test kapılarını +tanımlar. + +## 2. Mevcut sistem inceleme özeti + +Codebase incelemesinde çok dillilik açısından aşağıdaki mevcut durum tespit edildi: + +- `user_preferences.language` alanı bulunuyor ve varsayılanı `tr`. +- Aynı alanda SQLite check constraint yalnızca `tr` ve `en` değerlerine izin + veriyor. +- Preference service şu anda yalnızca `colorMode` döndürüyor; dil tercihini + runtime'a taşımıyor. +- Root layout içindeki `` sabit. +- Sidebar konfigürasyonları Türkçe başlıkları doğrudan taşıyor. +- Dashboard, ayarlar, auth ve portal ekranlarında kullanıcıya gösterilen metinler + bileşenlerin içine gömülü. +- Tarih ve para gösterimlerinde `tr-TR` ve `date-fns/locale/tr` doğrudan + kullanılıyor. +- Müşteri daveti oluşturulurken locale seçilmiyor veya davete kaydedilmiyor. +- `clients`, `app_profiles` ve `portal_invitations` üzerinde portal dili alanı yok. +- Proje, görev ve planlama alanlarının başlık/açıklamaları tek sütunda tutuluyor. +- Mobil discovery ve `/api/v1/meta` sözleşmesinde desteklenen diller ilan + edilmiyor. +- Arayüz metni içerme ihtimali bulunan yaklaşık 141 TypeScript/TSX dosyası var. + +Bu nedenle yalnızca bir çeviri kütüphanesi eklemek yeterli değildir. Arayüz +yerelleştirmesi, kullanıcı içeriği çevirileri, portal locale çözümleme ve API +sözleşmesi birlikte ele alınmalıdır. + +## 3. Temel kavramlar + +Bu projede iki farklı veri türü birbirinden kesin olarak ayrılacaktır. + +### 3.1. Arayüz çevirisi + +Uygulamanın kendisine ait metinlerdir: + +- Sidebar grup ve menü adları +- Sayfa başlıkları +- Buton, tab ve dialog metinleri +- Form label ve placeholder'ları +- Boş durum ve onay mesajları +- Toast ve kullanıcıya gösterilen hata metinleri +- Durum, öncelik ve kategori label'ları +- Login, kayıt ve davet ekranları +- Müşteri portalındaki sistem metinleri + +Bu metinler stabil anahtarlarla çağrılır: + +```ts +t("navigation.projects") +t("projects.form.name.label") +t("status.project.completed") +``` + +Türkçe ve İngilizce kaynak katalogları kod içinde sürümlenir. Instance sahibinin +yaptığı değişiklikler ve sonradan eklenen diller SQLite içinde saklanır. + +### 3.2. İçerik çevirisi + +Freelancer'ın oluşturduğu ve müşteriye gösterilebilecek iş verileridir: + +- Proje adı ve açıklaması +- Proje kapak görseli alt metni +- Proje planlama bölümü başlığı ve içeriği +- Müşteriye açık görev başlığı ve açıklaması +- Portal karşılama ve footer metinleri +- İleride teklif, sözleşme ve benzeri portal içerikleri + +Bu içerikler formdaki locale tab'larıyla girilir ve `content_translations` +tablosunda saklanır. + +### 3.3. Çevrilmeyecek alanlar + +Her form alanını dil başına çoğaltmak doğru değildir. Aşağıdaki alanlar ortak +kalır: + +- Tarih ve saat +- Tutar, para birimi ve vergi +- Durum, öncelik ve kategori enum değerleri +- Müşteri/proje ilişkileri +- İlerleme yüzdesi +- E-posta, telefon ve URL +- Dosya ve görselin kendisi +- Teknik ID'ler + +Bu alanların değerleri ortak tutulur; kullanıcıya gösterilen label'ları arayüz +sözlüğünden çevrilir. + +### 3.4. Yazıldığı dilde kalacak içerikler + +İletişim niteliğindeki kayıtlar otomatik olarak çoğaltılmayacaktır: + +- Müşterinin yazdığı revizyon talebi +- Chat mesajları +- Freelancer'ın özel günlüğü +- Müşteri aktivite notları + +Bu içeriklerde gerekirse `sourceLocale` metadata'sı saklanabilir; metnin kendisi +yazıldığı dilde gösterilir. Otomatik çeviri ayrı ve opsiyonel bir ürün özelliğidir. + +## 4. Kilitlenecek ürün kararları + +Faz 0 tamamlanırken aşağıdaki kararlar ADR ile kesinleştirilecektir. Bu planın +önerilen varsayımları şöyledir: + +- [x] Türkçe ve İngilizce silinemez built-in diller olacak. +- [x] İlk migration sonrasında instance varsayılan dili Türkçe olacak. +- [x] Yeni eklenen dil önce `draft`, ardından `active` durumuna alınacak. +- [x] Eksik çeviri halinde sayfada çeviri anahtarı değil fallback metni gösterilecek. +- [x] Yeni locale için instance sahibi `tr` veya `en` fallback dili seçecek. +- [x] Portal için kritik çevirileri tamamlanmayan dil müşteri dili olarak + yayınlanamayacak. +- [x] Dashboard ve portal route'larına `/tr`, `/en` gibi URL prefix'i eklenmeyecek. +- [x] Locale; kullanıcı tercihi, davet kaydı ve güvenli cookie üzerinden + çözümlenecek. +- [x] Arayüz çeviri sistemi için ağır bir runtime bağımlılığı eklenmeyecek; mevcut + `Intl`, React ve SQLite temelli küçük bir Neta i18n katmanı yazılacak. +- [x] İlk sürümde makine çevirisi zorunlu olmayacak. +- [x] Çeviri paketi JSON olarak içe ve dışa aktarılabilecek. +- [x] İçerik formlarında yalnızca çevrilebilir metin alanları locale tab'ları + içinde olacak. +- [x] Instance varsayılan dilindeki zorunlu alanlar boş bırakılamayacak. +- [x] Built-in Türkçe ve İngilizce katalogları release sırasında yüzde 100 + tamamlanmış olmak zorunda olacak. + +## 5. Hedef mimari + +```text +Kod ile gelen anahtar kataloğu + ├── tr katalog + └── en katalog + | + v +SQLite locale kayıtları + instance çeviri override'ları + | + v +Locale resolver + ├── Auth öncesi: davet / cookie / instance default + ├── Freelancer: user preference / instance default + └── Client: user preference / client portal locale / instance default + | + v +Translator + ├── Server Components + ├── Server Actions / hata kodları + ├── Client Components provider + └── API locale context + | + +--------------------------+ + | | + v v +Arayüz sözlüğü İçerik translation resolver +sidebar, label, hata project/task/planning/branding +``` + +### 5.1. Önerilen klasör yapısı + +```text +lib/i18n/ + types.ts + keys.ts + catalog.ts + format.ts + +locales/ + tr/ + common.ts + auth.ts + navigation.ts + dashboard.ts + clients.ts + projects.ts + tasks.ts + calendar.ts + finance.ts + journal.ts + chat.ts + settings.ts + portal.ts + validation.ts + en/ + ... + +server/i18n/ + locale.ts + resolver.ts + translator.ts + catalog.ts + service.ts + content.ts + validation.ts + +server/repositories/ + i18n.ts + content-translations.ts + +components/i18n/ + i18n-provider.tsx + locale-tabs.tsx + localized-fields.tsx + translation-status.tsx +``` + +### 5.2. Namespace standardı + +Katalog tek ve devasa bir JSON dosyası olmayacaktır. Anahtarlar modül bazında +namespace'lere ayrılır: + +| Namespace | Kapsam | +| --- | --- | +| `common` | Kaydet, sil, düzenle, iptal, loading, boş durumlar | +| `auth` | Login, register, davet, şifre ve auth hataları | +| `navigation` | Freelancer ve portal sidebar | +| `dashboard` | Freelancer dashboard | +| `clients` | Müşteri liste/detay/form | +| `projects` | Proje liste/detay/form/planlama | +| `tasks` | Görev liste/kanban/form | +| `calendar` | Takvim ve etkinlik | +| `finance` | Finans, analiz ve işlem formu | +| `journal` | Günlük ekranı | +| `chat` | AI sohbet ekranı | +| `settings` | Ayarlar ve dil yönetimi | +| `portal` | Müşteri portalı | +| `status` | Domain status/priority/type label'ları | +| `validation` | Form, action ve kullanıcıya açık hata metinleri | + +Her anahtar anlamlı ve stabil olmalıdır. Türkçe cümle anahtar olarak +kullanılmamalıdır. + +## 6. Locale çözümleme sözleşmesi + +### 6.1. Locale formatı + +- Locale kodları BCP 47 formatında kabul edilir: `tr`, `en`, `fr`, `de`, + `pt-BR`, `zh-CN`. +- Girdi `Intl.getCanonicalLocales()` ile normalize edilir. +- Aynı locale farklı harf biçimleriyle ikinci kez eklenemez. +- Locale kaydında gösterim adı, native ad ve yön (`ltr`/`rtl`) tutulur. +- Maksimum locale kodu ve metin uzunlukları merkezi Zod schema ile doğrulanır. + +### 6.2. Freelancer locale önceliği + +1. Giriş yapan kullanıcının `user_preferences.language` değeri +2. Güvenli ve aktif bir `neta_locale` cookie değeri +3. Instance varsayılan locale'i +4. Son güvenlik fallback'i olarak `tr` + +### 6.3. Müşteri portalı locale önceliği + +1. Müşteri hesabının `user_preferences.language` değeri +2. İlgili `clients.portal_locale` değeri +3. Instance varsayılan locale'i +4. Son güvenlik fallback'i olarak `tr` + +### 6.4. Davet ve auth öncesi locale önceliği + +1. Portal davetinin `locale` snapshot'ı +2. Aktif `neta_locale` cookie değeri +3. Instance varsayılan locale'i +4. Son güvenlik fallback'i olarak `tr` + +`Accept-Language` yalnızca public login ekranında, header'daki locale instance +üzerinde aktifse düşük öncelikli ilk ziyaret ipucu olarak kullanılabilir. Müşteri +davetinin seçilmiş dilini değiştiremez. + +### 6.5. Çeviri fallback zinciri + +Örnek seçili locale `fr-CA` ise: + +```text +fr-CA override + -> fr override + -> locale kaydında seçilen fallback (en veya tr) + -> built-in en + -> kontrollü missing-translation metni +``` + +Production ekranında `projects.form.name.label` gibi ham anahtarlar +gösterilmeyecektir. Development ve test ortamında missing key loglanacaktır. + +### 6.6. HTML ve formatlama + +- Root layout içindeki `lang` aktif locale'e göre üretilecek. +- Root layout içindeki `dir`, locale kaydındaki `ltr` veya `rtl` değerinden + üretilecek. +- Tarih/saat `Intl.DateTimeFormat(locale, options)` ile biçimlendirilecek. +- Para ve sayı `Intl.NumberFormat(locale, options)` ile biçimlendirilecek. +- `date-fns` gereken yerlerde locale mapping merkezi helper üzerinden seçilecek. +- Timezone tercihi locale'den bağımsız kalacak. +- Para birimi tercihi locale'den bağımsız kalacak. + +## 7. Hedef veri modeli + +Tablo ve kolon adları implementasyon sırasında Drizzle naming standardına göre +kesinleştirilir. + +### 7.1. `instance_locales` + +Instance üzerinde yönetilebilen dil listesidir. + +| Alan | Amaç | +| --- | --- | +| `code` | Canonical BCP 47 locale, primary key | +| `display_name` | Ayarlar ekranındaki yönetim adı | +| `native_name` | Dilin kendi dilindeki adı | +| `direction` | `ltr` veya `rtl` | +| `fallback_locale` | Eksik anahtarların düşeceği aktif locale | +| `status` | `draft`, `active`, `archived` | +| `is_builtin` | Türkçe ve İngilizce koruması | +| `sort_order` | Form tab sırası | +| `created_at` | Oluşturulma zamanı | +| `updated_at` | Güncellenme zamanı | + +Kurallar: + +- `tr` ve `en` migration ile eklenir. +- Built-in locale silinemez veya arşivlenemez. +- Varsayılan veya müşteri tarafından kullanılan locale doğrudan silinemez. +- Locale silmek yerine arşivleme tercih edilir. +- `fallback_locale`, kaydın kendisi olamaz ve döngü oluşturamaz. + +### 7.2. `instance_i18n_settings` + +Tek instance'a ait global dil ayarlarıdır. + +| Alan | Amaç | +| --- | --- | +| `id` | Sabit `default` kaydı | +| `owner_user_id` | Instance sahibi | +| `default_locale` | Yeni kullanıcılar ve genel fallback | +| `catalog_version` | Cache invalidation için artan sürüm | +| `updated_by_user_id` | Son değiştiren owner | +| `updated_at` | Son değişiklik | + +İlk migration `default_locale=tr` olarak seed eder. + +### 7.3. `instance_ui_translations` + +Built-in katalog override'larını ve sonradan eklenen locale metinlerini saklar. + +| Alan | Amaç | +| --- | --- | +| `id` | Teknik kimlik | +| `locale` | `instance_locales.code` ilişkisi | +| `namespace` | Katalog namespace'i | +| `translation_key` | Stabil anahtar | +| `value` | Çevrilmiş düz metin | +| `updated_by_user_id` | Değişikliği yapan owner | +| `created_at` | Oluşturulma zamanı | +| `updated_at` | Güncellenme zamanı | + +Unique constraint: + +```text +(locale, namespace, translation_key) +``` + +Built-in Türkçe ve İngilizce metinler kodda kalır. Bu tablo yalnızca değiştirilen +değerleri ve custom locale değerlerini taşır; böylece her katalog sürümünde +binlerce seed satırı oluşturulmaz. + +### 7.4. `content_translations` + +Portalda gösterilebilen domain içeriklerini normalize biçimde saklar. + +| Alan | Amaç | +| --- | --- | +| `id` | Teknik kimlik | +| `owner_user_id` | Authorization scope | +| `entity_type` | `project`, `task`, `planning_section`, `branding` vb. | +| `entity_id` | İlgili domain kaydı | +| `field` | `name`, `title`, `description`, `content`, `cover_image_alt` vb. | +| `locale` | İçeriğin locale'i | +| `value` | Çevrilmiş metin | +| `created_at` | Oluşturulma zamanı | +| `updated_at` | Güncellenme zamanı | + +Unique constraint: + +```text +(owner_user_id, entity_type, entity_id, field, locale) +``` + +Polymorphic `entity_id` nedeniyle DB seviyesinde her domain tablosuna foreign key +kurulamaz. Bütün erişim merkezi `ContentTranslationService` üzerinden yapılır: + +- Geçerli entity ve çevrilebilir alan registry üzerinden doğrulanır. +- Owner scope zorunludur. +- Domain kaydı silinirken çeviri kayıtları aynı transaction içinde silinir. +- Portal actor yalnızca kendisine açık kaynakların resolved metnini okuyabilir. +- Portal actor ham çeviri setini veya başka locale içeriklerini değiştiremez. + +### 7.5. Mevcut tablo değişiklikleri + +- `user_preferences.language` üzerindeki sabit `in ('tr', 'en')` constraint'i + kaldırılır. +- Bu alan yalnızca instance üzerinde aktif locale değerlerini service katmanında + kabul eder. +- `clients.portal_locale` alanı eklenir. +- `portal_invitations.locale` alanı eklenir. +- Davet kabul transaction'ında client locale ve yeni kullanıcının preference + locale'i birlikte yazılır. +- Gerekirse mevcut `owner_user_id` adlı preference kolonu şema kırmadan korunur; + kod tarafında bunun tüm auth kullanıcıları için preference sahibi anlamına + geldiği belgelenir. + +### 7.6. Eski içerik kolonları + +`projects.name`, `projects.description`, `tasks.title` gibi mevcut kolonlar ilk +sürümde kaldırılmayacaktır. + +- Migration mevcut değerleri Türkçe translation kayıtlarına backfill eder. +- Yeni create/update akışı varsayılan locale değerini hem eski kolona hem + `content_translations` tablosuna transaction içinde yazar. +- Eski kolonlar geriye uyumluluk ve güvenli rollback projection'ı olur. +- Portal ve yeni API okuma akışları locale-aware resolver kullanır. +- Bu kolonların tamamen kaldırılması ayrı bir major veri modeli kararıdır. + +## 8. Çevrilebilir domain alanları + +### 8.1. İlk zorunlu kapsam + +| Entity | Çevrilebilir alanlar | Portal etkisi | +| --- | --- | --- | +| `project` | `name`, `description`, `coverImageAlt` | Proje liste ve detay | +| `projectPlanningSection` | `title`, `content` | Proje planlama içeriği | +| `task` | `title`, `description` | Yalnız müşteriye açık görevler | +| `branding` | `portalWelcomeText`, `portalFooterText` | Portal shell/dashboard | + +### 8.2. İkinci kapsam + +| Entity | Çevrilebilir alanlar | Not | +| --- | --- | --- | +| `proposal` | `title`, `description` | Portalda yayınlandığında zorunlu | +| `contract` | `title`, `content` | Portalda yayınlandığında zorunlu | +| `invoice` | Açıklama/not alanı eklenirse | Mevcut şemada metin alanı yok | +| `calendarEvent` | `title`, `description` | Portal görünürlüğü eklenirse | + +### 8.3. Çevrilmeyecek domain alanları + +- Client adı ve firma adı kimlik verisidir; locale tab'ına girmez. +- Finans açıklaması owner-only olduğu sürece tek dilde kalır. +- Günlük notları ve AI chat içerikleri tek dilde kalır. +- Müşteri aktivite başlığı/içeriği owner-only olduğu sürece tek dilde kalır. +- Revizyon talebi müşterinin yazdığı dilde saklanır. + +Bu kapsam ileride portal görünürlüğü değiştiğinde registry üzerinden +genişletilebilir. + +## 9. Çok dilli form UX sözleşmesi + +Formlar Poyraz UI `Tabs` bileşeniyle ortak bir `LocalizedFields` bileşimi +kullanacaktır. + +Örnek proje formu: + +```text +[ Türkçe ✓ ] [ English ! ] [ Français ✓ ] + +Proje adı +[ Marka web sitesi ] + +Açıklama +[ ... ] + +------------------------------------------------ +Tür, müşteri, durum, tarih, bütçe, para birimi +``` + +Kurallar: + +- Aktif diller instance `sort_order` değerine göre tab olarak gösterilir. +- Varsayılan dil ilk tab olur ve “Varsayılan” işareti taşır. +- Eksik zorunlu alan bulunan tab uyarı noktası taşır. +- Dolu tab görsel tamamlanma işareti taşır. +- Tab değiştirmek form state'ini kaybettirmez. +- Input ID'leri locale ile ayrıştırılır. +- Form field adları merkezi parser'ın okuyacağı stabil yapıda olur: + `translations.tr.name`, `translations.en.name` gibi. +- Zorunlu alan doğrulaması varsayılan locale için yapılır. +- Müşteriye açık içerik kaydedilirken hedef portal locale'de eksik alan varsa + kullanıcıya fallback uygulanacağı açıkça gösterilir. +- Dil ekleme veya arşivleme açık bir formu bozmaz; form açılırken locale snapshot'ı + alınır. +- Mobil ve dar ekranlarda tab listesi yatay kayabilir; formun tamamı yatay scroll + oluşturmaz. +- Screen reader için tab, panel, label ve validation ilişkileri korunur. + +## 10. Dil yönetimi UX sözleşmesi + +Ayarlar sayfasına `Diller ve çeviriler` bölümü eklenir. + +### 10.1. Dil listesi + +Her dil satırında: + +- Native ad ve locale kodu +- Built-in/custom bilgisi +- Draft/active/archived durumu +- Genel çeviri tamamlanma yüzdesi +- Portal kritik çeviri tamamlanma yüzdesi +- Varsayılan dil işareti +- Düzenle, dışa aktar, aktif et/arşivle aksiyonları + +### 10.2. Dil ekleme + +Dil ekleme dialog'unda: + +- Locale kodu +- Görünen ad +- Native ad +- Yazı yönü +- Fallback dili + +yer alır. Yeni dil `draft` oluşturulur ve henüz müşterilere atanamaz. + +### 10.3. Çeviri editörü + +- Namespace filtresi +- Anahtar veya kaynak metin arama +- Yalnız eksik çevirileri gösterme +- Türkçe ve İngilizce referans metinlerini yan yana görme +- Hedef locale input'u +- Tek alan kaydetme ve toplu kaydetme +- Fallback'ten gelen değer ile gerçek override ayrımını görme +- Değeri sıfırlayarak built-in/fallback metnine dönme +- Portal için kritik anahtar filtresi + +### 10.4. JSON içe/dışa aktarma + +Önerilen format: + +```json +{ + "schemaVersion": 1, + "locale": "fr", + "fallbackLocale": "en", + "catalogVersion": 1, + "translations": { + "common.save": "Enregistrer", + "navigation.projects": "Projets" + } +} +``` + +İçe aktarma: + +- Owner yetkisi gerektirir. +- Dosya boyutu ve entry sayısı sınırlandırılır. +- Bilinmeyen key'ler raporlanır ve varsayılan olarak yazılmaz. +- Geçersiz veya tehlikeli anahtarlar reddedilir. +- Mevcut değerlerin üzerine yazma için açık onay gerekir. +- Import özeti ekleme/değiştirme/atlama sayılarıyla gösterilir. + +## 11. Portal dili ve davet akışı + +Yeni davet akışı: + +1. Freelancer müşteri detayında “Portal hesabı aç” dialog'unu açar. +2. E-posta ile birlikte `Portal dili` seçer. +3. Dropdown yalnızca aktif ve portal kritik çevirileri hazır dilleri gösterir. +4. Seçim `portal_invitations.locale` alanına snapshot olarak yazılır. +5. Aynı seçim `clients.portal_locale` alanına kaydedilir. +6. Müşteri davet linkini açtığında hesap oluşturma ekranı bu dilde görünür. +7. Davet kabul transaction'ı Better Auth user, profile, client bağlantısı ve + `user_preferences.language` kaydını atomik oluşturur. +8. Müşteri login olduğunda portal aynı locale ile açılır. +9. Freelancer daha sonra müşteri detayından portal dilini değiştirebilir. +10. Dil arşivlenecekse bu dili kullanan müşteriler için yeni locale seçilmeden + işlem tamamlanmaz. + +Davet yenilendiğinde yeni davet seçilen son locale'i taşır. Eski davet revoke +edilirken locale audit metadata'sına eklenir. + +## 12. API ve mobil istemci sözleşmesi + +Çok dillilik yalnızca web UI davranışı olmayacaktır. + +### 12.1. Discovery ve metadata + +`/api/v1/meta` additive olarak aşağıdaki bilgileri döndürür: + +```json +{ + "localization": { + "defaultLocale": "tr", + "supportedLocales": [ + { + "code": "tr", + "nativeName": "Türkçe", + "direction": "ltr" + }, + { + "code": "en", + "nativeName": "English", + "direction": "ltr" + } + ] + } +} +``` + +Capability listesine aşağıdaki kayıt eklenir: + +```json +{ + "id": "instance.localization", + "version": 1, + "status": "available", + "access": "public" +} +``` + +### 12.2. Kullanıcı bilgisi + +`GET /api/v1/me`: + +- Çözülmüş `preferences.language` değerini döndürür. +- Client rolünde `portalLocale` bilgisini döndürür. +- Locale'in arşivlenmesi gibi edge case'lerde çözülmüş fallback locale'i döndürür. + +### 12.3. Gelecek resource endpoint'leri + +- `Accept-Language` destekler. +- Gerekirse açık `?locale=fr` parametresi destekler. +- Client actor yalnızca kendi atanmış/izin verilen locale görünümünü alır. +- Freelancer actor isterse `includeTranslations=true` ile bütün edit setini alır. +- Varsayılan resource response resolved metni döndürür. +- Mutation sözleşmesi `translations: { locale: { field: value } }` yapısını + kullanır. +- Bilinmeyen locale için stabil `UNSUPPORTED_LOCALE` hata kodu döner. +- API'nin `error.code` alanı stabil kalır; `error.message` seçili locale'de + üretilebilir. + +### 12.4. Cache sözleşmesi + +- Public meta cache'i dil listesi değişince revalidate edilir. +- Locale bağımlı public yanıtlar `Vary: Accept-Language` taşır. +- Authenticated resource yanıtları `private, no-store` kalır. +- Mobil istemci katalog sürümünü saklayabilir; değiştiğinde ilgili locale + sözlüğünü yeniler. + +## 13. Güvenlik ve veri bütünlüğü + +- Locale ve çeviri yönetimi yalnızca freelancer/owner rolüne açıktır. +- UI translation değerleri varsayılan olarak düz metin render edilir. +- Çeviri editörü key üzerinden key dışı kod veya HTML çalıştırmaz. +- Rich text gerekirse ayrı sanitize edilmiş alan tipi olarak tasarlanır. +- Interpolation yalnızca tanımlı değişkenlere izin verir. +- Kullanıcı tarafından girilen `{{...}}` token'ları katalog şemasına göre + doğrulanır. +- Her çeviri değeri için maksimum uzunluk tanımlanır. +- JSON import için byte ve entry limiti uygulanır. +- `__proto__`, `constructor`, path traversal benzeri key'ler reddedilir. +- Fallback döngüleri service katmanında engellenir. +- Aktif kullanımda olan locale hard delete edilemez. +- İçerik çevirileri her read/write işleminde owner ve actor scope'u ile + sınırlandırılır. +- Client başka müşteriye ait translation kayıtlarını enumerate edemez. +- Davet locale'i sadece aynı owner'ın aktif locale listesinden seçilebilir. +- Locale değişiklikleri auth audit veya ayrı settings audit kaydına yazılır. + +## 14. Performans ve cache yaklaşımı + +- Built-in `tr/en` katalogları statik import ile gelir. +- DB yalnızca override ve custom locale değerlerini taşır. +- Server translator, request başına resolved dictionary oluşturur. +- Client layout'a yalnız ihtiyaç duyduğu namespace'leri alır. +- Tüm katalog her sayfada browser'a gönderilmez. +- `catalog_version` her locale/translation mutation'ında transaction içinde + artırılır. +- Cache anahtarı `locale + namespace set + catalogVersion` olur. +- Dil değişikliğinde ilgili layout ve metadata revalidate edilir. +- İçerik resolver liste sorgularında N+1 oluşturmaz; entity ID listesi ve locale + zinciri için tek toplu sorgu kullanır. +- Portal proje/görev listelerinde translation join/batch ölçümü yapılır. +- SQLite index'leri locale ve entity lookup'larına göre eklenir. + +## 15. Faz bazlı uygulama planı + +### Faz 0 — Envanter, ADR ve test fixture'ları + +Amaç: Kod yazmadan önce kapsamı ve davranış sözleşmelerini kilitlemek. + +#### Görevler + +- [x] Kullanıcıya görünen sabit metinlerin dosya ve namespace envanterini çıkar. +- [x] Freelancer ve portal sayfalarını ayrı migration listelerine ayır. +- [x] Tüm sabit `tr-TR`, `` ve `date-fns/locale/tr` + kullanımlarını kaydet. +- [x] Çevrilebilir domain alan registry'sini kesinleştir. +- [x] UI çevirisi ile domain içerik çevirisi ayrımını ADR olarak yaz. +- [x] Locale çözümleme önceliğini ADR olarak yaz. +- [x] Route prefix kullanılmaması kararını kaydet. +- [x] Custom runtime i18n katmanı kararını ve bağımlılık etkisini kaydet. +- [x] Türkçe mevcut veri fixture'ı hazırla. +- [x] İngilizce ve eksik Fransızca katalog fixture'ı hazırla. +- [x] Freelancer, Türkçe client ve İngilizce client auth fixture'ları hazırla. +- [x] RTL davranışı için test locale'i belirle. +- [x] Baseline build, typecheck ve ilgili smoke sonuçlarını kaydet. + +#### Faz 0 çıktıları + +- [Faz 0 ADR seti](i18n-phase-0/adr.md) +- [Faz 0 i18n envanteri](i18n-phase-0/inventory.md) +- [Faz 0 fixture planı](i18n-phase-0/fixtures.md) +- [Faz 0 migration ve geri dönüş sözleşmesi](i18n-phase-0/migration-contract.md) +- [Faz 0 baseline sonuçları](i18n-phase-0/baseline.md) + +#### Çıkış kriteri + +- [x] Katalog anahtar standardı onaylandı. +- [x] İlk çevrilebilir entity/field listesi onaylandı. +- [x] Fallback ve portal yayın kuralları tartışmasız hale geldi. +- [x] Migrasyon öncesi geri dönüş ve backup adımları yazıldı. + +### Faz 1 — Locale veri modeli ve service katmanı + +Amaç: Dil yönetiminin güvenli SQLite temelini oluşturmak. + +#### Görevler + +- [x] `instance_locales` Drizzle şemasını ekle. +- [x] `instance_i18n_settings` Drizzle şemasını ekle. +- [x] `instance_ui_translations` Drizzle şemasını ekle. +- [x] `content_translations` Drizzle şemasını ekle. +- [x] `clients.portal_locale` alanını ekle. +- [x] `portal_invitations.locale` alanını ekle. +- [x] `user_preferences.language` sabit check constraint'ini kaldır. +- [x] Gerekli unique constraint ve index'leri ekle. +- [x] Türkçe ve İngilizce locale seed'lerini migration içine ekle. +- [x] Instance varsayılan dilini `tr` olarak seed et. +- [x] Mevcut preference kayıtlarını doğrula ve normalize et. +- [x] Locale CRUD repository ve service katmanını yaz. +- [x] Locale ekleme, aktif etme, arşivleme ve default değiştirme validasyonlarını + yaz. +- [x] Fallback döngüsü ve referanslı locale korumasını uygula. +- [x] Owner/client negatif authorization testlerini yaz. +- [x] Backup/restore smoke testine yeni tabloları dahil et. + +#### Faz 1 çıktıları + +- [Faz 1 locale veri modeli ve service raporu](i18n-phase-1.md) +- `server/db/schema/i18n.ts` +- `server/repositories/i18n.ts` +- `server/i18n/service.ts` +- `server/db/migrations/0008_wild_mercury.sql` +- `scripts/i18n-phase1-smoke.mjs` +- `scripts/i18n-phase1-smoke.ts` + +#### Çıkış kriteri + +- [x] Mevcut veritabanı kayıpsız migrate oluyor. +- [x] Yeni instance `tr` ve `en` ile açılıyor. +- [x] Geçersiz locale ve fallback döngüsü DB'ye yazılamıyor. +- [x] Aktif kullanılan locale yanlışlıkla silinemiyor. +- [x] Mevcut login, dashboard ve portal akışları değişmeden çalışıyor. + +### Faz 2 — Çeviri runtime'ı ve built-in kataloglar + +Amaç: Server ve client component'lerde ortak, typed ve fallback destekli çeviri +altyapısını kurmak. + +#### Görevler + +- [x] Katalog key/namespace tiplerini tanımla. +- [x] Built-in Türkçe katalogları mevcut metinlerden çıkar. +- [x] İngilizce katalogları eksiksiz hazırla. +- [x] Server-side `createTranslator(locale, namespaces)` helper'ını yaz. +- [x] Client `I18nProvider` ve `useTranslations` hook'unu yaz. +- [x] Interpolation formatını ve doğrulamasını uygula. +- [x] Basit çoğul kurallarını `Intl.PluralRules` ile uygula. +- [x] DB override + built-in + fallback merge sırasını uygula. +- [x] Locale resolver'ı cookie/session/instance kaynaklarına bağla. +- [x] `neta_locale` cookie lifecycle'ını ekle. +- [x] Root layout `lang` ve `dir` değerlerini dinamik yap. +- [x] Ortak tarih, saat, para ve sayı formatter'larını yaz. +- [x] Missing key development log'u ve production fallback davranışını uygula. +- [x] Namespace bazlı cache ve `catalog_version` invalidation'ını uygula. +- [x] Türkçe/İngilizce katalog parity testini yaz. + +#### Faz 2 çıktıları + +- [Faz 2 çeviri runtime raporu](i18n-phase-2.md) +- `lib/i18n/*` +- `locales/tr/*` +- `locales/en/*` +- `components/i18n/i18n-provider.tsx` +- `server/i18n/catalog.ts` +- `server/i18n/locale.ts` +- `server/i18n/resolver.ts` +- `server/i18n/translator.ts` +- `server/i18n/runtime.ts` +- `app/api/i18n/locale/route.ts` +- `scripts/i18n-phase2-smoke.mjs` +- `scripts/i18n-phase2-smoke.ts` + +#### Çıkış kriteri + +- [x] Aynı component Türkçe ve İngilizce render edilebiliyor. +- [x] Custom locale override deployment olmadan okunabiliyor. +- [x] Eksik Fransızca anahtar belirlenen fallback ile gösteriliyor. +- [x] `` ve `` locale'e göre doğru. +- [x] Client hydration mismatch oluşmuyor. +- [x] Built-in katalog key setleri yüzde 100 eşleşiyor. + +### Faz 3 — Ayarlar: dil ve çeviri yönetimi + +Amaç: Instance sahibinin kod değiştirmeden dil ekleyip yönetebilmesini sağlamak. + +#### Görevler + +- [x] Ayarlar sidebar'ına `Diller ve çeviriler` bölümü ekle. +- [x] Dil listesi ve tamamlanma kartlarını Poyraz UI ile oluştur. +- [x] Yeni dil ekleme dialog'unu ekle. +- [x] Default locale değiştirme akışını ekle. +- [x] Giriş yapan freelancer için kişisel arayüz dili seçicisini ekle ve + `user_preferences.language` alanına bağla. +- [x] Draft/active/archive lifecycle'ını ekle. +- [x] Namespace ve eksik anahtar filtreli çeviri editörünü ekle. +- [x] Türkçe/İngilizce referans metinlerini editörde göster. +- [x] Tekil ve toplu çeviri kaydetme action'larını yaz. +- [x] Built-in override'ı sıfırlama aksiyonunu ekle. +- [x] Genel ve portal kritik tamamlanma yüzdesini hesapla. +- [x] JSON export endpoint/action'ını yaz. +- [x] Güvenli JSON import preview ve commit akışını yaz. +- [x] Çeviri değişikliklerinde layout/meta/cache revalidation uygula. +- [x] Ayarlar mutation'larını audit et. +- [x] Yetkisiz client erişimi için negatif test ekle. + +#### Faz 3 çıktıları + +- [Faz 3 ayarlar dil ve çeviri yönetimi raporu](i18n-phase-3.md) +- `app/(dashboard)/settings/page.tsx` +- `app/(dashboard)/settings/actions.ts` +- `server/i18n/service.ts` +- `server/repositories/i18n.ts` +- `server/settings/preferences.ts` +- `scripts/i18n-phase3-smoke.mjs` +- `scripts/i18n-phase3-smoke.ts` + +#### Çıkış kriteri + +- [x] Owner Ayarlar'dan `fr` ekleyebiliyor. +- [x] Fransızca bir sidebar metni girilip sayfa yenilemeden sonra kullanılabiliyor. +- [x] Eksik metinler editörde bulunabiliyor. +- [x] JSON export/import round-trip veri kaybetmiyor. +- [x] Client dil yönetimi endpoint/action'larına erişemiyor. + +### Faz 4 — Freelancer arayüzünün sayfa sayfa taşınması + +Amaç: Owner uygulamasındaki bütün sistem metinlerini katalog üzerinden göstermek. + +#### Ortak bileşenler + +- [x] App shell, skip link ve mobil menü +- [x] Sidebar grup ve menüleri +- [x] Hesap dropdown'ı ve çıkış +- [x] Page header, stat card ve empty/error state +- [x] Status badge ve confirmation dialog +- [x] Toast, pending button ve genel validation mesajları +- [x] Metadata description ve erişilebilirlik label'ları + +#### Sayfalar + +- [x] Login +- [x] İlk admin kaydı +- [x] Dashboard +- [x] Takvim +- [x] Analizler +- [x] Müşteriler listesi +- [x] Müşteri detayı ve portal daveti +- [x] Projeler listesi +- [x] Proje detayı +- [x] Görevler +- [x] Finans +- [x] Günlük +- [x] AI sohbet +- [x] Teklifler +- [x] Faturalar +- [x] Abonelikler +- [x] Ayarlar + +#### Formatlama ve hata sözleşmesi + +- [x] Tüm sabit `tr-TR` kullanımını locale-aware formatter'a taşı. +- [x] Tüm sabit `date-fns` Türkçe locale importlarını merkezi mapping'e taşı. +- [x] Enum değerlerini çevirmeden, yalnız gösterim label'larını çevir. +- [x] Server action'larda kullanıcıya gösterilen string yerine stabil hata kodu + döndürmeye başla. +- [x] Hata kodlarını `validation` namespace'i üzerinden kullanıcı locale'inde + göster. +- [x] AI endpoint teknik hata detaylarını korurken UI mesajlarını locale-aware yap. + +#### Faz 4 çıktıları + +- [Faz 4 freelancer UI migration raporu](i18n-phase-4.md) +- [Faz 4 kalan hardcoded metin raporu](i18n-phase-4-hardcoded-text-report.md) +- `components/layout/app-shell.tsx` +- `components/layout/dashboard-shell.tsx` +- `config/sidebar.ts` +- `app/(dashboard)/layout.tsx` +- `app/(dashboard)/dashboard-client.tsx` +- `lib/i18n/browser.ts` +- `lib/i18n/date-fns.ts` +- `scripts/i18n-phase4-boundary.mjs` + +#### Çıkış kriteri + +- [x] Freelancer UI Türkçe ve İngilizce eksiksiz kullanılabiliyor. +- [x] Sidebar ve tüm header'lar seçili dilde. +- [x] Dialog, toast, empty state ve form doğrulamaları seçili dilde. +- [x] Tarih, sayı ve para formatları seçili locale'e uyuyor. +- [x] Hardcoded kullanıcı metni boundary kontrolü kalan istisnaları raporluyor. + +### Faz 5 — Çok dilli domain içerikleri ve form tab'ları + +Amaç: Freelancer'ın müşteriye gösterilecek içeriği aktif dillerde girebilmesini +sağlamak. + +#### Altyapı + +- [x] Çevrilebilir entity/field registry'sini kodla. +- [x] `ContentTranslationService` ve repository batch sorgularını yaz. +- [x] Locale fallback'li content resolver'ı yaz. +- [x] Create/update/delete transaction entegrasyonunu yaz. +- [x] `LocalizedFields` ve `LocaleTabs` ortak bileşenlerini yaz. +- [x] FormData translation parser ve Zod doğrulamasını yaz. +- [x] Eksik tab göstergesi ve default locale zorunluluğunu ekle. +- [x] Mevcut Türkçe içeriği `tr` kayıtlarına idempotent backfill et. +- [x] Eski temel kolon ile varsayılan locale projection'ını transaction içinde + senkron tut. + +#### Formlar + +- [x] Proje oluşturma formu: ad, açıklama, görsel alt metni +- [x] Proje düzenleme formu: ad, açıklama, görsel alt metni +- [x] Planlama alanı oluşturma/düzenleme: başlık ve içerik +- [x] Görev oluşturma/düzenleme: başlık ve açıklama +- [x] Proje detayından görev ekleme: başlık ve açıklama +- [x] Genel ayarlar: portal karşılama ve footer metni +- [x] İkinci kapsam aktifse teklif ve sözleşme formları — bu fazda aktif + kapsam dışı bırakıldı. + +#### Okuma akışları + +- [x] Freelancer liste/detayında kendi locale'ine resolved metin göster. +- [x] Edit formlarında tüm locale değerlerini batch yükle. +- [x] Arama davranışını tanımla: ilk sürümde aktif owner locale + temel kolon. +- [x] AI context üretirken source/default locale davranışını belirle. +- [x] İçerik silinince translation kayıtlarını aynı transaction'da sil. + +#### Çıkış kriteri + +- [x] Proje adı Türkçe, İngilizce ve Fransızca ayrı kaydedilebiliyor. +- [x] Tab değişimi girilmiş değeri kaybettirmiyor. +- [x] Varsayılan dil başlığı boş proje oluşturulamıyor. +- [x] Aynı proje seçili locale'e göre farklı resolved başlık döndürüyor. +- [x] Locale translation query'leri liste ekranında N+1 oluşturmuyor. +- [x] Eski veriler migration sonrasında Türkçe olarak görünmeye devam ediyor. + +#### Faz 5 çıktıları + +- [Faz 5 çok dilli domain içerikleri raporu](i18n-phase-5.md) +- `lib/i18n/content.ts` +- `server/i18n/content.ts` +- `components/i18n/localized-fields.tsx` +- `app/(dashboard)/projects/actions.ts` +- `app/(dashboard)/tasks/actions.ts` +- `app/(dashboard)/projects/page.tsx` +- `app/(dashboard)/projects/[id]/page.tsx` +- `app/(dashboard)/tasks/page.tsx` +- `scripts/i18n-phase5-smoke.mjs` +- `scripts/i18n-phase5-smoke.ts` + +### Faz 6 — Müşteri portal dili ve davet akışı + +Amaç: Müşterinin ilk davet ekranından itibaren kendisine atanmış dili görmesini +sağlamak. + +#### Davet + +- [x] Portal hesabı açma dialog'una dil dropdown'u ekle. +- [x] Yalnız portal-ready aktif dilleri seçilebilir yap. +- [x] Compatibility `/api/create-client-user` adapter'ına locale ekle. +- [x] `/api/portal-invitations` sözleşmesine locale ekle. +- [x] Invitation schema ve service validasyonuna locale ekle. +- [x] Locale'i invitation ve client kaydına transaction içinde yaz. +- [x] Davet preview response'una güvenli locale bilgisini ekle. +- [x] Davet sayfasını invitation locale'i ile render et. +- [x] Davet kabulünde `user_preferences.language` kaydını oluştur. +- [x] Invitation audit metadata'sına locale ekle. + +#### Portal shell ve sayfalar + +- [x] Portal sidebar +- [x] Portal hesap dropdown'ı ve çıkış +- [x] Portal dashboard +- [x] Portal projeler listesi +- [x] Portal proje detayı +- [x] Portal public görev listesi +- [x] Portal revizyon listesi +- [x] Revizyon oluşturma formu +- [x] Portal progress ve empty state'ler +- [x] Portal tarih, sayı ve durum label'ları + +#### İçerik çözümleme + +- [x] Proje ve planlama içeriklerini client locale'inde resolve et. +- [x] Public görevleri client locale'inde resolve et. +- [x] Eksik içerikte belirlenen fallback zincirini uygula. +- [x] Revizyon talebini yazıldığı dilde sakla. +- [x] Client başka locale'lerin edit setine erişemediğini test et. + +#### Yönetim + +- [x] Müşteri detayında mevcut portal dilini göster. +- [x] Portal dili değiştirme aksiyonu ekle. +- [x] Locale arşivleme öncesi etkilenen müşteri sayısını göster. +- [x] Client locale değişikliğinde aktif session'ın sonraki request'te yeni dili + kullanmasını sağla. + +#### Çıkış kriteri + +- [x] İngilizce seçilen davet linki İngilizce açılıyor. +- [x] Hesap kabulü sonrasında portal İngilizce kalıyor. +- [x] Fransızca client, Fransızca proje içeriğini görüyor. +- [x] Eksik Fransızca içerik güvenli fallback ile gösteriliyor. +- [x] Türkçe client davranışında regression yok. +- [x] Başka müşterinin locale veya çeviri verisi sızmıyor. + +#### Faz 6 çıktıları + +- [Faz 6 müşteri portal dili ve davet akışı raporu](i18n-phase-6.md) +- `server/auth/invitations.ts` +- `app/api/create-client-user/route.ts` +- `app/api/portal-invitations/route.ts` +- `app/api/portal-clients/[clientId]/locale/route.ts` +- `app/invite/[token]/page.tsx` +- `app/portal/layout.tsx` +- `components/layout/portal-shell.tsx` +- `config/portal-sidebar.ts` +- `app/portal/page.tsx` +- `app/portal/projects/page.tsx` +- `app/portal/projects/[id]/page.tsx` +- `app/portal/tasks/page.tsx` +- `app/portal/revisions/page.tsx` + +### Faz 7 — Auth, hata, bildirim ve erişilebilirlik bütünlüğü + +Amaç: Ana sayfalar dışında kalan uç metinleri ve locale edge case'lerini +tamamlamak. + +#### Görevler + +- [x] Login ekranında aktif diller arasında seçim sun. +- [x] İlk admin kurulumunda instance default locale davranışını tamamla. +- [x] Forgot/reset password ekranlarını kataloglaştır. +- [x] 404, error boundary ve maintenance metinlerini kataloglaştır. +- [x] Server action redirect query mesajlarını stabil hata kodlarına taşı. +- [x] Toast tekrarlarını engelleyen mevcut akışları locale değişiminde doğrula. +- [x] Erişilebilirlik label, `aria-label`, tooltip ve screen-reader metinlerini + kataloglaştır. +- [x] Interpolation ve plural örneklerini tüm built-in locale'lerde test et. +- [x] RTL smoke testi yap; desteklenmeyen layout noktalarını raporla ve düzelt. +- [x] Locale değiştirme kontrolünün focus ve klavye davranışını test et. +- [x] Bildirim/e-posta sistemi eklendiğinde kullanacağı locale sözleşmesini + belgeye bağla. + +#### Çıkış kriteri + +- [x] Auth öncesi ve sonrası locale geçişi tutarlı. +- [x] Kullanıcıya görünen server hata mesajları Türkçe'ye gömülü değil. +- [x] Erişilebilirlik metinleri de seçili locale'de. +- [x] RTL locale temel shell ve formları kullanılamaz hale getirmiyor. + +### Faz 8 — API v1 ve mobil hazırlık + +Amaç: Aynı dil modelini gelecekteki React Native istemcisi için stabil bir +sözleşmeye dönüştürmek. + +#### Görevler + +- [x] `instance.localization` capability kaydını ekle. +- [x] `/api/v1/meta` localization alanlarını ekle. +- [x] `/.well-known/neta` için gerekli additive locale bilgisini değerlendir. +- [x] `/api/v1/me` preference language ve portal locale alanlarını ekle. +- [x] `Accept-Language` parser ve locale negotiation helper'ını yaz. +- [x] Gelecek resource endpoint'leri için localized response contract'ı ekle. +- [x] Owner mutation contract'ında `translations` shape'ini standartlaştır. +- [x] `UNSUPPORTED_LOCALE` hata kodunu API response mapping'e ekle. +- [x] Client'ın unknown locale/capability değerlerini güvenli ele almasını + belgeye ekle. +- [x] Meta cache revalidation ve absolute URL davranışını test et. +- [x] API contract fixture'larını `tr`, `en`, `fr` için ekle. +- [x] Phase 9 mobile smoke testlerini localization alanlarıyla genişlet. + +#### Faz 8 çıktıları + +- [Faz 8 API v1 ve mobil hazırlık raporu](i18n-phase-8.md) +- `server/api/v1/localization.ts` +- `server/api/v1/contracts.ts` +- `scripts/i18n-phase8-smoke.mjs` +- `scripts/i18n-phase8-smoke.ts` +- `app/api/v1/me/route.ts` +- `scripts/phase9-api-boundary.mjs` +- `docs/self-hosted-redesign/i18n-phase-8-fixtures/` + +#### Çıkış kriteri + +- [x] Mobil istemci instance'ın desteklediği dilleri keşfedebiliyor. +- [x] `/me` kullanıcının çözülmüş dilini döndürüyor. +- [x] Eski v1 client'lar additive alanlar nedeniyle kırılmıyor. +- [x] Locale seçimi server authorization sınırını aşmıyor. + +### Faz 9 — Hardening, migrasyon ve release + +Amaç: Çok dilli özelliği self-host production kurulumu için güvenli şekilde +yayınlamak. + +#### Migrasyon + +- [x] Release öncesi otomatik backup zorunluluğunu belgeye ekle. +- [x] Boş DB migration testi yap. +- [x] Mevcut production benzeri Türkçe DB migration testi yap. +- [x] Tekrar çalışan idempotent backfill testi yap. +- [x] Migration failure sonrası eski sürüme dönüş prosedürünü test et. +- [x] Backup/restore sonrasında locale ve çeviri bütünlüğünü doğrula. +- [x] Supabase import aracının yeni locale default'larını doğru oluşturduğunu test + et. + +#### Otomasyon + +- [x] `phase-i18n:boundary` script'i ekle. +- [x] Hardcoded kullanıcı metni istisna listesini minimumda tut. +- [x] Built-in katalog parity ve interpolation değişken testlerini CI'a ekle. +- [x] Locale service authorization testlerini CI'a ekle. +- [x] Content translation transaction testlerini CI'a ekle. +- [x] Portal davet locale E2E testini CI'a ekle. +- [x] Build, typecheck, lint ve mevcut phase smoke testlerini çalıştır. +- [x] Docker standalone image içinde built-in katalogların bulunduğunu doğrula. + +#### Performans ve gözlem + +- [x] Freelancer dashboard render süresini önce/sonra ölç. +- [x] Portal proje listesi query sayısını önce/sonra ölç. +- [x] Custom katalog yükleme boyutunu ölç. +- [x] Missing translation sayacını logla; hassas içerik loglama. +- [x] Catalog version/cache invalidation yarış koşullarını test et. + +#### Dokümantasyon + +- [x] Self-host kurulum dokümanına default locale ayarını ekle. +- [x] Dil ekleme ve çeviri import/export rehberi yaz. +- [x] Portal müşterisine dil atama rehberi yaz. +- [x] Mobil API localization sözleşmesini güncelle. +- [x] Backup/restore dokümanına translation tablolarını ekle. +- [x] Release note ve upgrade uyarısını yaz. + +#### Çıkış kriteri + +- [x] Türkçe ve İngilizce kataloglar yüzde 100 tamamlandı. +- [x] Fransızca custom locale uçtan uca smoke testi geçti. +- [x] Davet öncesi, davet, login ve portal locale zinciri doğrulandı. +- [x] Mevcut Türkçe veri kaybı veya görünüm regression'ı yok. +- [x] Docker/Dokploy persistent volume backup-restore testi geçti. +- [x] Mobil v1 sözleşmesi geriye uyumlu. +- [x] Release readiness raporu yazıldı. + +## 16. Test matrisi + +| Senaryo | Beklenen sonuç | +| --- | --- | +| Yeni kurulum | `tr` ve `en` aktif, default `tr` | +| Mevcut DB upgrade | Eski içerik Türkçe translation olarak backfill | +| Owner dili İngilizce | Freelancer UI ve formatlar İngilizce | +| Eksik Fransızca UI key | Tanımlı fallback metni, ham key değil | +| Fransızca proje içeriği | Fransızca client Fransızca metni görür | +| Eksik Fransızca proje alanı | Default içerik fallback'i gösterilir | +| İngilizce portal daveti | Davet kabul sayfası İngilizce | +| Davet kabulü | Client preference dili invitation locale olur | +| Client tekrar login | Portal dili korunur | +| Locale arşivleme | Referanslı client varsa yönlendirme istenir | +| Translation import | Preview sonrası geçerli key'ler yazılır | +| Zararlı import key'i | Validation ile reddedilir | +| Client translation endpoint'i | 403/404 ile reddedilir | +| Başka client projesi | Locale değişse de erişilemez | +| RTL test locale'i | `dir=rtl`, shell ve form kullanılabilir | +| Backup/restore | Locale, overrides ve içerik çevirileri korunur | +| Eski mobil client | Yeni additive meta alanlarını yok sayarak çalışır | + +## 17. Riskler ve azaltma yaklaşımı + +### 17.1. Hardcoded metin kaçakları + +Risk: Bazı toast, tooltip veya empty state metinleri Türkçe kalabilir. + +Azaltma: + +- Namespace bazlı sayfa checklist'i +- Boundary script +- Türkçe karakter/string taraması +- İngilizce E2E ekran gezintisi + +### 17.2. Client/server locale uyuşmazlığı + +Risk: Server Türkçe, client İngilizce render ederek hydration hatası oluşturabilir. + +Azaltma: + +- Locale'i layout server tarafında bir kez çözmek +- Client provider'a resolved locale ve namespace snapshot vermek +- İlk render öncesi browser dilini bağımsız yeniden seçmemek + +### 17.3. Translation sorgularında N+1 + +Risk: Liste ekranlarında her proje için ayrı query çalışabilir. + +Azaltma: + +- Batch resolver +- Composite index +- Query-count smoke testi + +### 17.4. Eksik müşteri çevirisi + +Risk: Müşteri portalı kısmen farklı dillerde görünebilir. + +Azaltma: + +- Portal kritik namespace kapsamı +- Publish readiness yüzdesi +- Davet dropdown'unda yalnız portal-ready locale +- İçerik alanlarında eksik tab uyarısı +- Belirgin ve deterministik fallback + +### 17.5. Eski kolon ve translation tablosu ayrışması + +Risk: Bir write yalnız tablolardan birini günceller. + +Azaltma: + +- Tek service mutation noktası +- Transaction +- Repository'ye doğrudan UI erişimini boundary testiyle engelleme +- Tutarlılık kontrol script'i + +### 17.6. Custom çeviride XSS veya bozuk interpolation + +Risk: Owner'ın girdiği metin HTML veya sahte placeholder ile render'ı bozabilir. + +Azaltma: + +- Düz metin varsayımı +- Placeholder schema doğrulaması +- HTML render etmeme +- Import limitleri ve güvenli key parser + +## 18. Önerilen commit parçalama stratejisi + +Her faz kendi içinde rollback edilebilir küçük commit'lere ayrılmalıdır: + +1. Şema, migration ve repository +2. Service, validation ve test +3. Ortak UI/runtime altyapısı +4. Sayfa/form entegrasyonları +5. Dokümantasyon ve phase checklist güncellemesi + +Şema migration'ı ile onu kullanan runtime kodu ayrı deploy'larda uyumsuz hale +gelmemelidir. Gerekirse expand/migrate/contract sırası izlenir. + +## 19. Genel tamamlanma checklist'i + +- [x] Faz 0 — Envanter, ADR ve fixture +- [x] Faz 1 — Locale veri modeli ve service +- [x] Faz 2 — Runtime ve built-in kataloglar +- [x] Faz 3 — Dil/çeviri yönetimi +- [x] Faz 4 — Freelancer UI migration +- [x] Faz 5 — Çok dilli içerik ve form tab'ları +- [x] Faz 6 — Portal dili ve davet +- [x] Faz 7 — Auth/hata/a11y bütünlüğü +- [x] Faz 8 — API ve mobil hazırlık +- [x] Faz 9 — Hardening ve release + +## 20. Nihai definition of done + +Bu özellik aşağıdaki maddelerin tamamı sağlanmadan bitmiş sayılmaz: + +- [ ] Türkçe ve İngilizce yeni kurulumda hazır gelir. +- [ ] Owner Ayarlar'dan üçüncü bir dil ekleyebilir. +- [ ] Owner custom dilde bütün sistem metinlerini düzenleyebilir. +- [ ] Sidebar, başlıklar, butonlar, formlar, durumlar ve hatalar seçili dilde + gösterilir. +- [ ] Tarih, saat, sayı ve para locale'e göre biçimlenir. +- [ ] Çevrilebilir formlarda aktif diller tab olarak görünür. +- [ ] Proje ve müşteriye açık görev içerikleri dil başına saklanır. +- [ ] Portal davetinde müşteri dili seçilir. +- [ ] Davet kabul ekranı seçilen dilde açılır. +- [ ] Müşteri her login sonrasında portalı atanmış dilde görür. +- [ ] Portal içerikleri müşteri locale'ine göre çözülür. +- [ ] Eksik çeviri davranışı deterministik ve güvenlidir. +- [ ] Client authorization locale parametresiyle aşılamaz. +- [ ] Mobil metadata desteklenen dilleri ilan eder. +- [ ] Backup/restore bütün custom dilleri ve çevirileri korur. +- [ ] Mevcut Türkçe veriler migration sırasında kaybolmaz. +- [ ] Build, typecheck, lint, i18n boundary ve E2E smoke testleri geçer.