--- 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.