--- 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: in_progress current_phase: "faz-12" last_updated: 2026-07-20 supersedes: "neta-multilingual-i18n-v1-legacy-plan.md" --- # Neta Çok Dilli Sistem V2 Ana Planı ## 1. Neden yeni bir plan gerekiyor? İ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. Kod incelemesinde doğrulanan temel problemler: - 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. Ö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. Ürün hedefi Neta'yı self-host eden kişi: - 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. Portal kullanıcısı: - İ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. ## 3. Kesin ürün kararları ### 3.1. Auth ekranlarında dil davranışı - 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. ### 3.2. Owner/freelancer locale önceliği ```text user_preferences.language -> instance_i18n_settings.default_locale -> built-in tr ``` - Owner dilini `/settings/language` sayfasından değiştirir. - Seçim yalnız `active` diller arasından yapılır. - Locale cookie'si dil kararında kullanılmaz; authenticated tercih veritabanından ve public dil instance ayarından çözülür. ### 3.3. Portal locale önceliği ```text portal kullanıcısının user_preferences.language -> clients.portal_locale (adminin belirlediği başlangıç dili) -> instance default locale -> built-in tr ``` - 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. ### 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 status.ts ``` ### 6.2. Katalog kalite kapıları - `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. ## 7. Dinamik içerik çevirisi sözleşmesi Arayüz metni ile kullanıcının oluşturduğu domain içeriği ayrı kalacaktır. ### 7.1. Form davranışı - 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. - 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. ### 7.2. Çevrilebilir alan matrisi | 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 | ### 7.3. Kaynak dilinde saklanacak iletişim içeriği Aşağıdaki içerikler çeviri tab'ıyla çoğaltılmaz; çünkü bir kişinin yazdığı mesajdır: - Chat mesajı - Portal revizyon talebi - Gelecekte e-posta/yorum/mesaj kayıtları 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. ### 7.4. Legacy kolon ve translation tablosu - 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. ## 8. Teknik mimari düzeltmeleri ### 8.1. Resolver'ları ayır Tek `resolveRequestLocale()` fonksiyonu bütün bağlamları tahmin etmeye çalışmayacaktır. ```text resolvePublicLocale() resolveInvitationLocale(token) resolveFreelancerLocale(session) resolvePortalLocale(session, client) ``` - 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. - 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. - [x] Auth ekranlarındaki locale select kullanımını test fixture'ıyla kaydet. - [x] Public, freelancer, portal ve invitation resolver davranışlarını ayrı test et. - [x] Tüm route/page/client/action/loading/error dosyalarının kullanıcı metni envanterini çıkar. - [x] Her sayfa için mevcut katalog key kapsamını ve hard-coded metin sayısını raporla. - [x] Tüm domain create/edit formlarını ve metinsel alanları envanterle. - [x] Portalda görünen ve owner-only kalan alanları işaretle. - [x] V1 veri modeli için korunacak, düzeltilecek ve kaldırılacak parçaları ADR olarak yaz. - [x] TR/EN/custom locale seed ve regression fixture'larını hazırla. - [x] Sayfa bazlı i18n smoke runner oluştur. - [x] Baseline build, typecheck ve lint sonuçlarını kaydet. Faz 0 çıktıları: - [Mimari kararlar](i18n-v2-phase-0/adr.md) - [Sayfa ve içerik envanteri](i18n-v2-phase-0/inventory.md) - [TR/EN/custom regression fixture'ı](i18n-v2-phase-0/fixtures.json) - [Baseline ve regression kapıları](i18n-v2-phase-0/baseline.md) Çıkış kriteri: - [x] Hiçbir route veya kullanıcıya açık UI yüzeyi envanter dışında kalmadı. - [x] Her sonraki fazın ölçülebilir hard-coded-text baseline'ı var. ### Faz 1 — Locale resolver ve preference semantiğini düzelt - [x] Public, invitation, freelancer ve portal resolver'larını ayır. - [x] Public auth akışından cookie ve browser locale önceliğini kaldır. - [x] Freelancer için preference -> instance default zincirini uygula. - [x] Portal için preference -> client default -> instance default zincirini uygula. - [x] Locale cookie'sini resolver ve dil değiştirme akışından kaldır. - [x] Arşivlenmiş/eksik locale fallback davranışını tanımla. - [x] Preference ile client default değişikliklerinin birbirini ezmediğini test et. - [x] `` ve `dir` değerinin doğru resolved locale'den geldiğini test et. Çıkış kriteri: - [x] Dört bağlamın resolver testleri bağımsız geçiyor. - [x] Login sayfası eski locale cookie'sinden etkilenmiyor. ### Faz 2 — Settings route iskeleti ve kalıcı iç sidebar - [x] `/settings` route'unu `/settings/general` sayfasına yönlendir. - [x] Settings layout ve sticky iç sidebar oluştur. - [x] Desktop ve mobil settings navigasyonunu uygula. - [x] Aktif route, loading, not-found ve error sınırlarını ekle. - [x] Owner-only menü öğelerini yetkiye göre sınırla. - [x] Tek sayfalık client-state tab yapısını kaldırmaya hazır action sınırlarını ayır. - [x] Settings shell'in TR/EN kataloglarını tamamla. Çıkış kriteri: - [x] Refresh ve deep-link her settings alt route'unda çalışıyor. - [x] İç sidebar uzun sayfada sabit kalıyor. ### Faz 3 — Ayarlar / Genel sayfası Route: `/settings/general` - [x] Workspace adı, meta title, short name ve genel instance alanlarını taşı. - [x] Portal welcome/footer için aktif dil tab'larını uygula. - [x] Branding create/update action'ını route'a özel hale getir. - [x] Form, validation ve toast metinlerini TR/EN tamamla. - [x] Workspace metadata revalidation davranışını koru. - [x] Owner authorization sınırını koru. Çıkış kriteri: - [x] Genel sayfasında hard-coded kullanıcı metni kalmadı. - [x] Branding içeriklerinin tüm aktif dil değerleri kalıcı. ### Faz 4 — Ayarlar / Görünüm sayfası Route: `/settings/appearance` - [x] Light/dark/system kişisel tercihini taşı. - [x] Primary color ve light/dark logo yönetimini doğru owner kapsamına taşı. - [x] Kişisel görünüm ile instance branding kontrollerini görsel olarak ayır. - [x] Preview, upload, reset ve validation metinlerini çevir. - [x] Dark/light, responsive ve hydration-safe davranışı test et. Çıkış kriteri: - [x] Görünüm sayfası TR/EN eksiksiz ve tema geçişinde hydration hatasız. ### Faz 5 — Ayarlar / Profil sayfası Route: `/settings/profile` - [x] Ad, soyad ve avatar formunu taşı. - [x] Account bilgisi, upload ve toast metinlerini çevir. - [x] Kişisel profile action'ını owner-only instance action'lardan ayır. - [x] Validation ve hata kodlarını locale-aware hale getir. Çıkış kriteri: - [x] Profil sayfasında tüm kullanıcı metinleri resolved locale ile geliyor. ### Faz 6 — Ayarlar / Güvenlik sayfası Route: `/settings/security` - [x] Şifre değiştirme formunu taşı. - [x] Mevcut/yeni şifre validation ve hata mesajlarını çevir. - [x] Session revoke davranışını açık ve erişilebilir biçimde göster. - [x] Success/error toast'larını tek kaynak üzerinden üret. Çıkış kriteri: - [x] Güvenlik akışı TR/EN ve custom fallback locale ile çalışıyor. ### Faz 7 — Ayarlar / Yapay zekâ sayfası Route: `/settings/ai` - [x] Provider, model ve API key kontrollerini taşı. - [x] Secret mask, mevcut key, değiştirme ve hata UX'ini düzenle. - [x] AI provider ve bağlantı hata metinlerini çevir. - [x] Owner-only authorization'ı doğrula. Çıkış kriteri: - [x] AI ayarlarında sabit Türkçe/İngilizce metin yok. ### Faz 8 — Ayarlar / Kişisel dil tercihi sayfası Route: `/settings/language` - [x] Yalnız aktif dilleri göster. - [x] Kişisel tercih ile instance default bilgisini ayrı göster. - [x] Preference mutation'ı uygula ve layout'u güvenli yenile. - [x] Arşivlenmiş preference için fallback ve yeniden seçim uyarısı ekle. - [x] Dil adlarını native name + locale code ile göster. Çıkış kriteri: - [x] Owner dili yalnız bu sayfadan değiştirilebiliyor. - [x] Login/auth ekranlarında dil seçici bulunmuyor. ### Faz 9 — Ayarlar / Diller liste sayfası Route: `/settings/languages` - [x] Yeni dil yönetim listesi UX'ini uygula. - [x] Default, status, fallback, completion ve usage sütunlarını ekle. - [x] Instance default locale kontrolünü bu sayfaya taşı. - [x] Draft/active/archived filtrelerini ekle. - [x] Loading, empty, error ve permission state'lerini çevir. Çıkış kriteri: - [x] Dil listesi çeviri editörüyle aynı ekranda değil. - [x] Default dil ve kişisel dil tercihi birbirine karışmıyor. ### Faz 10 — Ayarlar / Yeni dil sayfası Route: `/settings/languages/new` - [x] BCP 47 locale formunu uygula. - [x] Native ad, yönetim adı, direction ve fallback alanlarını ekle. - [x] Custom dilin daima draft oluşmasını sağla. - [x] Duplicate, self-fallback ve fallback-loop hatalarını çevir. - [x] Başarılı kayıtta dil detayına yönlendir. Çıkış kriteri: - [x] Geçersiz locale DB'ye ulaşmadan reddediliyor. ### Faz 11 — Ayarlar / Dil detay sayfası Route: `/settings/languages/[locale]` - [x] Metadata ve lifecycle yönetimini uygula. - [x] Namespace/sayfa bazlı completion özetini ekle. - [x] Kullanıcı/client kullanım etkisini göster. - [x] Activate/default/archive readiness kontrollerini uygula. - [x] Built-in korumalarını UI ve service katmanında test et. Çıkış kriteri: - [x] 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.