diff --git a/docs/self-hosted-redesign/i18n-phase-0/adr.md b/docs/self-hosted-redesign/i18n-phase-0/adr.md new file mode 100644 index 0000000..a3ad850 --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-0/adr.md @@ -0,0 +1,171 @@ +--- +title: Faz 0 ADR Seti +description: Neta cok dilli sisteminin baslangic karar kayitlari. +phase: 0 +status: accepted +last_updated: 2026-07-19 +--- + +# Faz 0 ADR Seti + +Bu dosya cok dillilik calismasinin ilk uygulama kararlarini kilitler. Amac, +sonraki fazlarda ayni problemi tekrar tekrar tartismadan ilerleyebilmek ve +SQLite tabanli self-host hedefini korumaktir. + +## ADR-I18N-0001 — UI cevirisi ve domain icerik cevirisi ayrilacak + +### Durum + +Kabul edildi. + +### Baglam + +Neta'da iki farkli metin tipi var: + +- Uygulamanin kendisine ait sabit arayuz metinleri. +- Freelancer veya musteri tarafindan uretilen is icerikleri. + +Bu iki veri tipini ayni tabloda veya ayni anahtar modeliyle tutmak, hem fallback +hem de izin modeli tarafinda karmasa yaratir. + +### Karar + +Arayuz cevirileri stabil katalog anahtarlariyla yonetilecek. Built-in `tr` ve +`en` kataloglari kod icinde surumlenecek; instance sahibi tarafindan eklenen +override'lar SQLite icindeki `instance_ui_translations` modelinde tutulacak. + +Domain icerik cevirileri entity ve field bazli tutulacak. Bunun icin Faz-1'de +`content_translations` modeli eklenecek. + +### Sonuc + +- Arayuz metinleri release sureciyle denetlenebilir kalir. +- Sonradan eklenen diller deployment gerektirmeden doldurulabilir. +- Is icerikleri icin form tab'lari net bicimde modellenir. +- Chat, gunluk notu ve musteri mesajlari gibi iletisim icerikleri otomatik + cevrilmez; yazildigi dilde kalir. + +## ADR-I18N-0002 — Locale cozumleme onceligi + +### Durum + +Kabul edildi. + +### Baglam + +Ayni instance icinde freelancer, musteri portali, davet ekranlari ve public API +farkli baglamlarda calisir. Locale cozumleme her yerde ayni kaynaga bakarsa +portal daveti gibi auth oncesi akislarda yanlis dil gorunebilir. + +### Karar + +Locale asagidaki sirayla cozumlenecek. + +Auth oncesi davet ekranlari: + +1. Davet kaydindaki locale. +2. Guvenli locale cookie'si. +3. Instance varsayilan dili. +4. Built-in fallback `tr`. + +Freelancer uygulamasi: + +1. Oturum acmis kullanicinin `user_preferences.language` degeri. +2. Guvenli locale cookie'si. +3. Instance varsayilan dili. +4. Built-in fallback `tr`. + +Musteri portali: + +1. Musteri profilinin veya `clients.portal_locale` alaninin locale degeri. +2. Portal davetinden gelen locale. +3. Guvenli locale cookie'si. +4. Instance varsayilan dili. +5. Built-in fallback `tr`. + +Mobil ve API istekleri: + +1. Auth kullanicisinin kayitli tercihi. +2. Guvenilir endpoint parametresi veya `Accept-Language` sinyali. +3. Instance varsayilan dili. +4. Built-in fallback `tr`. + +### Sonuc + +- Musteri davet ekrani login olmadan dogru dilde acilir. +- Freelancer tercihi musteri portalini yanlislikla etkilemez. +- Mobil istemci ayni kurallari API kontratindan okuyabilir. + +## ADR-I18N-0003 — Dashboard ve portal route'larina locale prefix eklenmeyecek + +### Durum + +Kabul edildi. + +### Baglam + +`/tr/dashboard`, `/en/dashboard` gibi route prefix'leri public marketing +sitelerinde gucludur, ancak Neta'nin dashboard ve portal yapisi oturum, davet, +yetki ve self-host basitligi uzerine kurulu. Prefix eklemek route sayisini, +redirect davranislarini ve mobil endpoint eslemesini gereksiz buyutur. + +### Karar + +Dashboard, portal ve davet route'lari mevcut URL yapisini koruyacak. Dil, locale +resolver ve cookie/user/client kayitlariyla belirlenecek. + +### Sonuc + +- Var olan internal linkler ve mobil baglanti modeli korunur. +- Self-host deploy ve reverse proxy ayarlari sade kalir. +- Public landing/docs tarafi isterse ileride ayri route prefix stratejisi + kullanabilir; bu karar sadece uygulama kabugu icindir. + +## ADR-I18N-0004 — Hafif custom runtime i18n katmani yazilacak + +### Durum + +Kabul edildi. + +### Baglam + +Projenin ana hedeflerinden biri dependency sayisini azaltmak ve self-host'u +kolaylastirmak. Mevcut ihtiyaclar temel katalog cozumleme, fallback, namespace +yukleme, `Intl` bicimlendirme ve SQLite override okuma uzerinden karsilanabilir. + +### Karar + +Ilk cok dillilik surumunde agir bir i18n runtime dependency'si eklenmeyecek. +Neta icinde kucuk bir i18n katmani yazilacak: + +- `lib/i18n/*` ortak tip ve format yardimcilari. +- `server/i18n/*` locale resolver, katalog merge ve translator servisleri. +- Client component'ler icin sinirli scope'ta provider. +- Tarih, sayi ve para bicimlendirme icin native `Intl`. + +### Sonuc + +- Yeni dependency eklenmeden Faz-2 runtime kurulabilir. +- Katalog sozlesmesi Neta'nin domain ihtiyaclarina gore sekillenir. +- Ileride ihtiyac buyurse baska kutuphaneye gecis icin katalog anahtarlari + korunabilir. + +## ADR-I18N-0005 — Built-in diller ve yayin kurali + +### Durum + +Kabul edildi. + +### Karar + +Turkce (`tr`) ve Ingilizce (`en`) built-in, aktif ve silinemez diller olacak. +Instance varsayilan dili ilk migration sonrasinda `tr` olacak. + +Yeni eklenen diller once `draft` durumunda olusturulacak. Portal icin kritik +namespace'leri tamamlanmayan bir dil musteri dili olarak secilemeyecek. + +### Sonuc + +- Eksik ceviri musteriye yarim bir portal deneyimi olarak yansimaz. +- Instance sahibi yeni dili hazirlayip test ettikten sonra aktif edebilir. +- Fallback davranisi her zaman tahmin edilebilir kalir. diff --git a/docs/self-hosted-redesign/i18n-phase-0/baseline.md b/docs/self-hosted-redesign/i18n-phase-0/baseline.md new file mode 100644 index 0000000..8d04ee2 --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-0/baseline.md @@ -0,0 +1,44 @@ +--- +title: Faz 0 Baseline Sonuclari +description: Cok dillilik calismasi baslamadan onceki build, typecheck ve smoke referansi. +phase: 0 +status: completed +last_updated: 2026-07-19 +--- + +# Faz 0 Baseline Sonuclari + +Bu baseline, cok dillilik kod degisikliklerinden onceki saglik referansidir. +Sonraki fazlarda yeni hata olup olmadigini anlamak icin bu komut setiyle +karsilastirma yapilacak. + +## Komut sonuclari + +| Komut | Sonuc | Not | +| --- | --- | --- | +| `pnpm typecheck` | Basarili | `tsc --noEmit` hatasiz tamamlandi. | +| `pnpm build` | Basarili | Next.js 16.2.10 production build ve standalone hazirlama tamamlandi. | +| `pnpm phase9:smoke` | Basarili | API boundary, auth/invitation ve mobile API smoke kapilari gecti. | + +## Build notu + +`pnpm build` sirasinda su Next.js uyarisi goruldu: + +```text +Using edge runtime on a page currently disables static generation for that page +``` + +Bu Faz-0 icin yeni bir hata degil; mevcut route/runtime tercihinden kaynaklanan +uyari olarak kaydedildi. + +## Faz-1 icin baseline beklentisi + +Faz-1 sonunda en az asagidaki komutlar tekrar calistirilmelidir: + +- `pnpm typecheck` +- `pnpm build` +- Locale migration eklendikten sonra ilgili DB smoke komutu +- Varsa yeni `i18n` service unit/smoke komutu + +Faz-5 ve Faz-6 gibi domain/portal davranisi degisen fazlarda `phase6:smoke` ve +portal-specific fixture testleri de baseline setine eklenmelidir. diff --git a/docs/self-hosted-redesign/i18n-phase-0/fixtures.md b/docs/self-hosted-redesign/i18n-phase-0/fixtures.md new file mode 100644 index 0000000..186f25e --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-0/fixtures.md @@ -0,0 +1,181 @@ +--- +title: Faz 0 I18n Fixture Plani +description: Cok dillilik migration, katalog ve portal testleri icin seed senaryolari. +phase: 0 +status: completed +last_updated: 2026-07-19 +--- + +# Faz 0 I18n Fixture Plani + +Bu plan Faz-1 sonrasinda script veya test fixture'ina donusturulecek veri setini +tanımlar. Hedef, locale resolver, fallback, domain icerik cevirileri ve portal +dili davranisini ayni senaryoda dogrulamaktir. + +## Locale fixture'lari + +| Locale | Durum | Built-in | Fallback | Amac | +| --- | --- | --- | --- | --- | +| `tr` | `active` | Evet | Yok | Instance default ve mevcut verinin ana dili. | +| `en` | `active` | Evet | `tr` | Built-in ikinci dil ve portal client testi. | +| `fr` | `draft` | Hayir | `en` | Eksik katalog/fallback testi. | +| `ar-XB` | `test` | Hayir | `en` | RTL smoke ve layout kontrolu. | + +`tr` ve `en` silinemez. `fr` portal dili olarak ancak kritik portal namespace'leri +tamamlaninca secilebilir. `ar-XB` urun ayarlarinda normal kullaniciya +gosterilmez. + +## Katalog fixture'lari + +### Turkce built-in katalog + +Tum namespace'lerde eksiksiz olmalidir: + +- `common` +- `auth` +- `navigation` +- `dashboard` +- `clients` +- `projects` +- `tasks` +- `calendar` +- `finance` +- `journal` +- `chat` +- `settings` +- `portal` +- `status` +- `validation` +- `api` + +### Ingilizce built-in katalog + +Turkce ile ayni anahtar setine sahip olmalidir. Faz-2 testinde anahtar farki +release engeli kabul edilir. + +### Eksik Fransizca katalog + +Fallback test etmek icin bilincli olarak kismi tutulur. + +Ornek: + +```json +{ + "navigation.dashboard": "Tableau de bord", + "navigation.projects": "Projets", + "portal.dashboard.title": "Apercu", + "common.save": "Enregistrer" +} +``` + +Beklenen davranis: Eksik `portal.tasks.title` gibi anahtarlarda once `en`, sonra +`tr` fallback metni gosterilir; kullaniciya ham anahtar basılmaz. + +## Auth fixture'lari + +| Kullanici | Rol | Email | Dil | Baglam | +| --- | --- | --- | --- | --- | +| Freelancer | `admin` | `owner@neta.local` | `tr` | Dashboard, ayarlar ve ceviri yonetimi. | +| Turkce musteri | `client` | `client-tr@neta.local` | `tr` | Portal default dil testi. | +| Ingilizce musteri | `client` | `client-en@neta.local` | `en` | Portal client locale testi. | + +Freelancer hesabi instance default dilini `tr` olarak korur. Ingilizce musteri +icin davet kaydinda ve `clients.portal_locale` alaninda `en` beklenir. + +## Domain veri fixture'i + +### Musteriler + +| ID | Ad | Portal dili | Not | +| --- | --- | --- | --- | +| `client-tr` | Ada Yilmaz | `tr` | Turkce portal goruntusu. | +| `client-en` | Nova Studio | `en` | Ingilizce portal goruntusu. | + +### Projeler + +| ID | Musteri | Default baslik | EN baslik | FR baslik | Not | +| --- | --- | --- | --- | --- | +| `project-website` | `client-tr` | Web sitesi yenileme | Website refresh | Bos | Fallback testi. | +| `project-brand` | `client-en` | Marka kiti | Brand kit | Kit de marque | Portal EN ve FR content testi. | + +Beklenen davranis: + +- `tr` isteyen kullanici default kolon veya `content_translations(tr)` degerini + gorur. +- `en` isteyen kullanici EN content translation'i gorur. +- `fr` isteyen kullanici FR alan bos ise fallback zinciriyle EN veya TR gorur. + +### Gorevler + +| ID | Proje | Public | TR baslik | EN baslik | +| --- | --- | --- | --- | --- | +| `task-wireframe` | `project-website` | Evet | Ana sayfa wireframe | Homepage wireframe | +| `task-internal` | `project-website` | Hayir | Ic notlari toparla | Gather internal notes | + +Portal yalnizca `is_public_to_client = true` gorevleri gosterir. + +### Planlama bolumleri + +| ID | Proje | Kategori | TR baslik | EN baslik | +| --- | --- | --- | --- | --- | +| `plan-scope` | `project-website` | `scope` | Kapsam | Scope | +| `plan-delivery` | `project-website` | `delivery` | Teslimat | Delivery | + +Kategori teknik degeri cevrilmez; label `status` veya `projects` namespace'inden +gelir. + +## Portal davet fixture'lari + +| Davet | Email | Locale | Beklenen ekran dili | +| --- | --- | --- | --- | +| `invite-tr` | `client-tr@neta.local` | `tr` | Turkce | +| `invite-en` | `client-en@neta.local` | `en` | Ingilizce | +| `invite-fr-draft` | `client-en@neta.local` | `fr` | Portal publish kurali nedeniyle reddedilir veya fallback EN kullanir. | + +Davet ekranlari auth oncesi oldugu icin locale'i session'dan degil davet +kaydindan alir. + +## API contract fixture'lari + +`/api/v1/meta` Faz-8 sonunda en az su localization bilgisini dondurmelidir: + +```json +{ + "localization": { + "defaultLocale": "tr", + "supportedLocales": [ + { "code": "tr", "name": "Turkce", "status": "active", "builtIn": true }, + { "code": "en", "name": "English", "status": "active", "builtIn": true }, + { "code": "fr", "name": "Francais", "status": "draft", "builtIn": false } + ], + "fallbacks": { + "en": "tr", + "fr": "en" + } + } +} +``` + +Mobil istemci aktif olmayan dilleri kullanici seciminde gostermemeli, ancak +ceviri yonetimi ekranlari icin admin API'de draft dilleri gorebilmelidir. + +## Smoke beklentileri + +Faz-2 sonrasinda: + +- `t("common.save")` `tr` icin `Kaydet`, `en` icin `Save` dondurur. +- `fr` locale'inde eksik anahtar ham key olarak gosterilmez. +- `formatMoney(10000, "TRY", "tr")` Turkce bicim verir. +- `formatDate(..., "en")` Ingilizce ay adi verir. + +Faz-6 sonrasinda: + +- `client-en` portali EN navigasyon ve EN proje basliklariyla acilir. +- `client-tr` portali TR kalir. +- Davet kabul edildikten sonra secilen locale client kaydina yansir. + +Faz-8 sonrasinda: + +- `/api/v1/meta` supported locale listesini dondurur. +- `/api/v1/me` kullanicinin etkin locale'ini ve fallback chain'ini dondurur. +- API hata response'lari stabil kod + locale'e uygun insan okunur mesaj tasir. diff --git a/docs/self-hosted-redesign/i18n-phase-0/inventory.md b/docs/self-hosted-redesign/i18n-phase-0/inventory.md new file mode 100644 index 0000000..81db620 --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-0/inventory.md @@ -0,0 +1,149 @@ +--- +title: Faz 0 I18n Envanteri +description: Kullaniciya gorunen metinler, locale sabitleri ve namespace migration envanteri. +phase: 0 +status: completed +last_updated: 2026-07-19 +--- + +# Faz 0 I18n Envanteri + +Bu envanter Faz-1 ve Faz-2 icin kapsam sinirini belirler. Liste uygulama +kodunun mevcut durumuna gore hazirlandi; yeni sayfa eklendikce ayni namespace +modeliyle genisletilmelidir. + +## Namespace standardi + +Katalog anahtarlari `namespace.section.item` biciminde olacak. Turkce cumleler +anahtar olarak kullanilmayacak. + +| Namespace | Sahip oldugu alan | +| --- | --- | +| `common` | Kaydet, iptal, sil, ara, filtrele, yukleniyor, bos durum, toast eylemleri | +| `auth` | Login, register, setup, davet kabul, auth hata mesajlari | +| `navigation` | Freelancer ve portal sidebar grup/link label'lari | +| `dashboard` | Freelancer ana sayfa basliklari, stats, grafik ve son kayit kartlari | +| `clients` | Musteri liste, detay, form, portal hesap islemleri | +| `projects` | Proje liste, detay, form, planlama, revizyon ve risk analizi | +| `tasks` | Gorev liste, kanban, form, durum ve oncelik UI metinleri | +| `calendar` | Takvim, etkinlik kartlari, etkinlik formu | +| `finance` | Finans stats, islem formu, kategori, AI analiz modal'i | +| `journal` | Gunluk liste, form, mood/enerji label'lari | +| `chat` | Sohbet listesi, bos durum, hata ve input metinleri | +| `settings` | Genel gorunum, marka, AI, profil, dil yonetimi | +| `portal` | Musteri portal dashboard, projeler, gorevler, revizyonlar | +| `status` | Enum label'lari: status, priority, type, pipeline, payment | +| `validation` | Form/action hata ve basari mesajlari | +| `api` | Mobil/public API hata kodlari icin insan okunur mesajlar | + +## Freelancer migration listesi + +Faz-4'te asagidaki sirayla UI sabitleri katalog anahtarina alinacak. + +| Sira | Route veya dosya | Namespace'ler | Not | +| --- | --- | --- | --- | +| 1 | `config/sidebar.ts` | `navigation` | Sidebar link ve grup adlari merkezi baslangic noktasi. | +| 2 | `components/layout/app-shell.tsx` | `navigation`, `common`, `settings` | Account menu, tema ve layout metinleri. | +| 3 | `app/(dashboard)/page.tsx`, `dashboard-client.tsx` | `dashboard`, `common`, `status` | Ana stats, grafik bos durumlari ve tarih/para formatlari. | +| 4 | `app/(dashboard)/clients/*` | `clients`, `common`, `status`, `validation` | Liste, detay, portal hesabi, activity mesajlari. | +| 5 | `app/(dashboard)/projects/*` | `projects`, `common`, `status`, `validation` | Proje formu, planlama, revizyon ve risk analizi. | +| 6 | `app/(dashboard)/tasks/*` | `tasks`, `common`, `status`, `validation` | Liste/kanban ayrimi ve gorev action metinleri. | +| 7 | `app/(dashboard)/calendar/*` | `calendar`, `common`, `status` | Date formatting Faz-2 format yardimcilarina tasinir. | +| 8 | `app/(dashboard)/finance/*` | `finance`, `common`, `status`, `validation` | Para formatting ve AI modal metinleri. | +| 9 | `app/(dashboard)/journal/*` | `journal`, `common`, `validation` | Mood label'lari ve date formatter'lar. | +| 10 | `app/(dashboard)/chat/*`, `app/api/chat/route.ts` | `chat`, `api`, `validation` | Kullaniciya donen hata mesaji daha detayli hale getirilmisti; i18n anahtariyla baglanacak. | +| 11 | `app/(dashboard)/settings/*` | `settings`, `common`, `validation` | Dil yonetimi Faz-3'te eklenecegi icin en son genisletilir. | +| 12 | `app/(dashboard)/analytics/*`, `business/*` | `dashboard`, `finance`, `common` | Mevcut business ekranlari i18n kapsamina alinacak. | + +## Portal migration listesi + +Portal Faz-6'da musteri diliyle birlikte ele alinacak. + +| Sira | Route veya dosya | Namespace'ler | Not | +| --- | --- | --- | --- | +| 1 | `config/portal-sidebar.ts` | `navigation`, `portal` | Portal navigasyon label'lari. | +| 2 | `components/layout/portal-shell.tsx` | `portal`, `navigation`, `common` | Kullanici menu ve kabuk metinleri. | +| 3 | `app/portal/page.tsx` | `portal`, `dashboard`, `common` | Musteri dashboard stats ve bos durumlari. | +| 4 | `app/portal/projects/page.tsx` | `portal`, `projects`, `status` | Proje liste metinleri ve tarih formatlari. | +| 5 | `app/portal/projects/[id]/*` | `portal`, `projects`, `tasks`, `status`, `validation` | Cevrilebilir project/task content resolver kullanir. | +| 6 | `app/portal/tasks/page.tsx` | `portal`, `tasks`, `status` | Public gorev metinleri. | +| 7 | `app/portal/revisions/page.tsx` | `portal`, `projects`, `validation` | Revizyon talepleri yazildigi dilde kalir. | +| 8 | `app/invite/[token]/*` | `auth`, `portal`, `validation` | Auth oncesi davet locale'iyle render edilir. | + +## Auth ve API migration listesi + +| Alan | Dosya | Namespace | Not | +| --- | --- | --- | --- | +| Admin setup | `app/register/page.tsx`, `app/login/*` | `auth`, `validation` | Cift toast fix'i korunarak metinler kataloglanir. | +| Better Auth route | `app/api/auth/[...all]/route.ts` | `api`, `auth` | Kullaniciya acik hata mapping'i gerekir. | +| Portal invitations API | `app/api/portal-invitations/*` | `api`, `portal` | Locale parametresi Faz-6'da kontrata eklenir. | +| Mobile API v1 | `app/api/v1/*`, `server/api/v1/*` | `api`, `common` | Meta endpoint supported/default locale bilgisini dondurur. | +| Branding API | `app/api/branding/*` | `api`, `settings` | Workspace gorunum metinleri ve mobile consumption ayni kalir. | + +## Sabit locale kullanimlari + +Asagidaki kullanimlar Faz-2 format yardimcilariyla degistirilecek. + +| Kullanim | Dosyalar | +| --- | --- | +| `` | `app/layout.tsx` | +| `date-fns/locale/tr` | `app/portal/page.tsx`, `app/portal/projects/page.tsx`, `app/portal/tasks/page.tsx`, `app/portal/revisions/page.tsx`, `app/portal/projects/[id]/portal-project-client.tsx`, `app/(dashboard)/clients/clients-client.tsx`, `app/(dashboard)/clients/[id]/client-detail-client.tsx`, `app/(dashboard)/business/proposals/proposals-client.tsx`, `app/(dashboard)/business/invoices/invoices-client.tsx`, `app/(dashboard)/business/subscriptions/subscriptions-client.tsx` | +| `Intl.*("tr-TR")` | `dashboard-client.tsx`, `calendar-client.tsx`, `finance-client.tsx`, `journal-client.tsx`, `projects-client.tsx`, `project-detail-client.tsx`, `tasks-client.tsx`, business client'lari | +| `toLocaleDateString("tr-TR")` | `dashboard-client.tsx`, `project-detail-client.tsx` | +| `localeCompare(..., "tr")` | `app/(dashboard)/projects/page.tsx` | + +## Kullaniciya gorunen sabit metin kaynaklari + +Turkce karakter iceren veya dogrudan kullaniciya donen string barindirma ihtimali +en yuksek alanlar: + +| Klasor | Kapsam | +| --- | --- | +| `app/(dashboard)` | Freelancer ekranlarinin buyuk bolumu. | +| `app/portal` | Musteri portali. | +| `app/invite` | Auth oncesi portal daveti. | +| `app/login`, `app/register` | Auth ekranlari. | +| `app/api` | Kullaniciya donen JSON hata/basari metinleri. | +| `components/layout` | Sidebar, account menu ve shell metinleri. | +| `components/system` | Ortak page header, stat card ve empty/error state metinleri. | +| `config/sidebar.ts` | Freelancer navigasyon metinleri. | +| `config/portal-sidebar.ts` | Portal navigasyon metinleri. | +| `server/auth`, `server/services`, `server/ai` | Server action/API hata ve toast mesajlari. | + +## Cevrilebilir domain alan registry'si + +Ilk surumde asagidaki alanlar `content_translations` ile locale bazli +cevrilebilir kabul edilir. + +| Entity | Field | Zorunlu locale | Not | +| --- | --- | --- | --- | +| `projects` | `name` | Instance default | Mevcut kolon backfill kaynagi. | +| `projects` | `description` | Opsiyonel | Portal ve freelancer detayinda kullanilir. | +| `projects` | `coverImageAlt` | Opsiyonel | A11y icin portalda onemli. | +| `tasks` | `title` | Instance default | Public/private fark etmeksizin ayni alan modeli. | +| `tasks` | `description` | Opsiyonel | Musteriye acik gorevlerde portalda gosterilir. | +| `calendar_events` | `title` | Instance default | Etkinlik ortak alandir, ileride portal visibility eklenirse hazir. | +| `calendar_events` | `description` | Opsiyonel | | +| `clients` | `notes` | Opsiyonel | Ilk surumde internal kalir; ceviri desteklenebilir ama portalda gosterilmez. | +| `planning_sections` | `title` | Instance default | Proje planlama bolumleri icin. | +| `planning_sections` | `content` | Opsiyonel | Rich text degilse plain text olarak saklanir. | +| `branding/settings` | `workspaceName` | Instance default | UI/portal basliklari ve mobile meta icin. | +| `branding/settings` | `metaTitle` | Instance default | Browser metadata. | +| `branding/settings` | `metaDescription` | Opsiyonel | SEO/public metadata. | + +Ilk surumde cevrilmeyecek alanlar: + +- Chat mesajlari. +- Revizyon talepleri. +- Gunluk notlari. +- Finans islem aciklamalari. +- Maliyet, tarih, para birimi, yuzde ve enum teknik degerleri. + +## RTL test locale'i + +Ilk release hedefi LTR diller olsa da layout kirilmasini erkenden gormek icin +test locale'i `ar-XB` olarak belirlendi. + +Bu locale aktif urun dili olarak sunulmayacak. Faz-7'de `dir="rtl"` davranisini, +sidebar hizalamalarini, form tab'larini ve modal yerlesimlerini smoke etmek icin +fixture olarak kullanilacak. diff --git a/docs/self-hosted-redesign/i18n-phase-0/migration-contract.md b/docs/self-hosted-redesign/i18n-phase-0/migration-contract.md new file mode 100644 index 0000000..7e55b62 --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-0/migration-contract.md @@ -0,0 +1,84 @@ +--- +title: Faz 0 Migration ve Geri Donus Sozlesmesi +description: Cok dillilik calismasi icin expand/migrate/contract sirasi ve backup kurallari. +phase: 0 +status: completed +last_updated: 2026-07-19 +--- + +# Faz 0 Migration ve Geri Donus Sozlesmesi + +Bu sozlesme Faz-1'den itibaren veritabani degisikliklerinin nasil yapilacagini +tanımlar. Neta self-host bir urun oldugu icin migration'lar veri kaybi riski +tasimamali ve rollback hikayesi basit kalmalidir. + +## Uygulama sirasi + +1. Expand + +Yeni tablolar ve yeni nullable kolonlar eklenir. Mevcut kolonlar kaldirilmaz, +renamelenmez ve zorunlu hale getirilmez. + +2. Backfill + +Mevcut Turkce veriler instance default locale'i kabul edilerek +`content_translations` icine kopyalanir. Orijinal kolonlar okunabilir kalir. + +3. Dual read + +Okuma katmani once translation resolver'a bakar, eksikse mevcut kolona duser. +Bu asamada eski veriler ve yeni veriler ayni anda calisir. + +4. Dual write + +Formlar instance default locale alanini hem mevcut kolona hem translation +tablosuna yazar. Ek locale'ler sadece translation tablosuna yazilir. + +5. Contract + +Ancak en az bir release sonra eski kolonlarin kaldirilmasi veya tamamen internal +fallback haline getirilmesi tartisilir. Ilk cok dillilik fazlarinda contract +adimi yapilmayacak. + +## Backup adimlari + +Her schema migration oncesi: + +```bash +pnpm db:backup +``` + +Backup dosyasi deploy notuna yazilmalidir. Dokploy veya Docker deploy'da volume +path'i kontrol edilmeden migration calistirilmamalidir. + +## Geri donus adimlari + +Migration sonrasi kritik hata varsa: + +1. Uygulama yeni surumden onceki image/commit'e geri alinir. +2. SQLite dosyasi backup'tan restore edilir. +3. `pnpm db:restore ` veya host tarafindaki manuel restore adimi + kullanilir. +4. Restore sonrasi `pnpm phase9:smoke` ile health/API temel davranisi kontrol + edilir. + +## Faz bazli DB dokunuslari + +| Faz | DB degisikligi | Risk | Not | +| --- | --- | --- | --- | +| Faz-1 | Locale ve translation tablolari, `clients.portal_locale`, `portal_invitations.locale`, preference check constraint genisletme | Orta | Expand only. | +| Faz-2 | DB yok veya yalnizca katalog version seed'i | Dusuk | Runtime/cache agirlikli. | +| Faz-3 | UI translation CRUD | Orta | Admin-only mutation ve audit gerekli. | +| Faz-5 | Domain content translations dual write | Yuksek | Backfill ve resolver testleri sart. | +| Faz-6 | Portal locale davet akisi | Orta | Auth oncesi davet dili kritik. | +| Faz-8 | API contract genisletme | Dusuk/Orta | Backward compatible response alanlari eklenir. | + +## Non-goal + +Ilk cok dillilik release'inde asagidakiler yapilmayacak: + +- Mevcut `projects.name`, `tasks.title` gibi kolonlari kaldirmak. +- Otomatik makine cevirisi eklemek. +- Public route'lara locale prefix eklemek. +- Chat/gunluk/revizyon taleplerini otomatik cevirmek. +- Kullanici yazili icerigini farkli locale'e sessizce overwrite etmek. diff --git a/docs/self-hosted-redesign/i18n-phase-1.md b/docs/self-hosted-redesign/i18n-phase-1.md new file mode 100644 index 0000000..59c5007 --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-1.md @@ -0,0 +1,76 @@ +--- +title: Faz 1 Locale Veri Modeli ve Service Katmani +description: Cok dillilik icin SQLite semasi, seed, locale yonetimi servisi ve smoke sonuclari. +phase: 1 +status: completed +last_updated: 2026-07-19 +--- + +# Faz 1 Locale Veri Modeli ve Service Katmani + +Faz-1, cok dillilik icin runtime'a girmeden once gerekli kalici veri modelini +ve owner-only locale yonetimi kurallarini ekledi. + +## Eklenen veri modeli + +| Tablo veya kolon | Amac | +| --- | --- | +| `instance_locales` | Instance icindeki aktif, draft, archived ve test dillerini tutar. | +| `instance_i18n_settings` | Varsayilan locale ve katalog versiyonunu tutar. | +| `instance_ui_translations` | Built-in katalog ustune yazilacak instance UI cevirilerini tutar. | +| `content_translations` | Domain entity/field/locale bazli icerik cevirilerini tutar. | +| `clients.portal_locale` | Musteri portal dilini tutar. | +| `portal_invitations.locale` | Auth oncesi davet ekraninin dilini tutar. | +| `user_preferences.language` | Sabit `tr/en` constraint yerine 2-12 karakter locale kodu kabul eder. | + +Migration `tr` ve `en` built-in locale'lerini seed eder, varsayilan instance +dilini `tr` olarak olusturur ve mevcut preference verisini kayipsiz tasir. + +## Service kurallari + +`I18nService` ile asagidaki kurallar kilitlendi: + +- Locale yonetimi yalnizca freelancer/owner actor tarafindan yapilabilir. +- Disabled actor islem yapamaz. +- Locale kodu kisa BCP47 formatinda dogrulanir: `tr`, `en`, `fr`, `ar-XB`. +- `tr` ve `en` built-in, aktif ve arsivlenemez diller olarak korunur. +- Fallback locale mevcut ve arsivlenmemis olmak zorundadir. +- Locale kendi kendine fallback olamaz. +- Fallback zinciri dongu olusturamaz. +- Varsayilan dil yalnizca `active` locale olabilir. +- Default, fallback, user preference, client portal veya invitation tarafindan + kullanilan locale arsivlenemez. + +## Verification + +Calistirilan komutlar: + +```bash +pnpm typecheck +pnpm i18n:phase1-smoke +pnpm build +pnpm phase9:smoke +``` + +Sonuclar basarili. Build sirasinda mevcut Next edge runtime statik uretim uyarisi +tekrar goruldu; Faz-1 ile ilgili yeni bir hata degil. + +`i18n:phase1-smoke` su kapilari kontrol eder: + +- Yeni migration tablolarini ve kolonlarini dogrular. +- `tr/en` seed ve default `tr` ayarini dogrular. +- `user_preferences.language` alaninin `fr` gibi yeni locale degeri kabul + ettigini dogrular. +- Owner/client negatif authorization testlerini calistirir. +- Invalid locale, duplicate built-in locale, self fallback ve fallback cycle + durumlarini reddeder. +- Draft locale'in default yapilamadigini dogrular. +- Referansli ve built-in locale'in arsivlenemedigini dogrular. +- Backup alip restore eder; `instance_i18n_settings` ve `content_translations` + verisinin restore sonrasinda korundugunu dogrular. + +## Faz-2'ye kalan sinir + +Bu faz henuz UI metinlerini cevirmedi ve runtime translator eklemedi. Faz-2'de +built-in kataloglar, locale resolver, formatter ve client/server translator +katmani bu veri modelini kullanacak. diff --git a/docs/self-hosted-redesign/i18n-phase-2.md b/docs/self-hosted-redesign/i18n-phase-2.md new file mode 100644 index 0000000..935f61b --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-2.md @@ -0,0 +1,78 @@ +--- +title: Faz 2 Ceviri Runtime'i ve Built-in Kataloglar +description: Neta cok dillilik runtime, katalog, fallback, formatter, locale resolver ve smoke sonuclari. +phase: 2 +status: completed +last_updated: 2026-07-19 +--- + +# Faz 2 Ceviri Runtime'i ve Built-in Kataloglar + +Faz-2, Faz-1'de eklenen locale veri modelini calisan runtime katmanina bagladi. +Bu faz tum sayfa metinlerini henuz migrate etmez; sayfa sayfa UI migration Faz-4 +kapsaminda yapilacak. + +## Eklenen runtime parcalari + +| Alan | Dosya | +| --- | --- | +| I18n tipleri ve namespace listesi | `lib/i18n/types.ts` | +| Built-in katalog registry ve parity helper | `lib/i18n/catalog.ts` | +| Interpolation, plural, tarih, sayi ve para formatter | `lib/i18n/format.ts` | +| Client-safe translator | `lib/i18n/translator.ts` | +| React client provider ve hook'lar | `components/i18n/i18n-provider.tsx` | +| TR built-in katalog | `locales/tr/*` | +| EN built-in katalog | `locales/en/*` | +| Server catalog merge ve catalogVersion cache | `server/i18n/catalog.ts` | +| Locale cookie ve direction helper | `server/i18n/locale.ts` | +| Session/cookie/instance locale resolver | `server/i18n/resolver.ts` | +| Server `createTranslator` helper | `server/i18n/translator.ts` | +| Public localization metadata | `server/i18n/runtime.ts` | +| Locale cookie lifecycle route | `app/api/i18n/locale/route.ts` | + +## Davranis + +- Built-in `tr` ve `en` kataloglari ayni namespace/key setine sahiptir. +- Ceviri anahtarlari `namespace.key` formatinda kullanilir. +- Interpolation `{name}` formatini destekler. +- Basit plural formatinda `Intl.PluralRules` kullanilir. +- DB override, built-in katalog ustune merge edilir. +- Fallback sirasi locale kaydindaki fallback chain'e gore calisir. +- Eksik anahtar production'da ham key'e duser; development'ta console warning + verir. +- `catalog_version` override mutation'inda artar ve cache key'ine dahil edilir. +- `` ve `` request locale resolver'dan gelir. +- `neta_locale` cookie'si `/api/i18n/locale` route'u uzerinden yazilir. +- `/api/v1/meta` response'u localization metadata'si dondurur. + +## Verification + +Calistirilan komutlar: + +```bash +pnpm i18n:phase2-smoke +pnpm build +pnpm phase9:smoke +pnpm typecheck +``` + +Sonuclar basarili. `i18n:phase2-smoke` su kapilari dogrular: + +- TR/EN katalog parity. +- Ayni translation helper ile TR ve EN render. +- Interpolation ve plural format. +- Ortak tarih ve para formatter. +- RTL direction helper. +- Fransizca eksik anahtarda EN fallback. +- DB UI translation override'in deployment olmadan okunmasi. +- Override sonrasi `catalog_version` bump ile yeni metnin okunmasi. +- Owner disi actor'un UI translation yazamamasi. + +`pnpm build` sirasinda mevcut Next edge runtime static generation uyarisi tekrar +goruldu; Faz-2 kaynakli yeni bir hata degil. + +## Faz-4'e kalan sinir + +Bu fazda mevcut sayfa metinleri henuz topluca katalog anahtarlarina tasinmadi. +Dashboard, musteri, proje, gorev, finans, gunluk, chat ve portal ekranlarindaki +hardcoded metinlerin migration'i Faz-4 ve Faz-6 kapsaminda yapilacak. diff --git a/docs/self-hosted-redesign/i18n-phase-3.md b/docs/self-hosted-redesign/i18n-phase-3.md new file mode 100644 index 0000000..866088d --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-3.md @@ -0,0 +1,59 @@ +--- +title: Faz 3 Ayarlar Dil ve Ceviri Yonetimi +description: Ayarlar ekraninda locale yonetimi, UI translation editor, import/export ve dogrulama sonuclari. +phase: 3 +status: completed +last_updated: 2026-07-19 +--- + +# Faz 3 Ayarlar Dil ve Ceviri Yonetimi + +Faz-3, Faz-1 ve Faz-2 i18n altyapisini owner tarafindan yonetilebilir hale +getirdi. + +## Eklenenler + +- Ayarlar sayfasina `Diller ve ceviriler` tab'i eklendi. +- Owner yeni locale ekleyebilir. +- Locale `draft`, `active`, `archived` lifecycle'i UI uzerinden yonetilebilir. +- Aktif locale instance default dili yapilabilir. +- Freelancer kendi arayuz dilini secebilir; tercih `user_preferences.language` + ve `neta_locale` cookie'sine yazilir. +- Locale kartlarinda genel tamamlanma yuzdesi ve eksik anahtar sayisi gorunur. +- Namespace ve metin aramali UI translation editor eklendi. +- Editor Turkce ve Ingilizce referans metnini ayni satirda gosterir. +- Tekil translation override kaydedilebilir ve sifirlanabilir. +- JSON export paketi uretilebilir. +- JSON import preview ve commit akisi eklendi. +- I18n mutation'lari `runtime_events` icine `i18n.settings` olarak audit edilir. +- Import/export round-trip ve client negatif yetki smoke testi eklendi. + +## Degisen dosyalar + +| Alan | Dosya | +| --- | --- | +| Settings UI | `app/(dashboard)/settings/page.tsx` | +| Settings server actions | `app/(dashboard)/settings/actions.ts` | +| I18n service export/import/completion | `server/i18n/service.ts` | +| UI translation repository helpers | `server/repositories/i18n.ts` | +| User language preference service | `server/settings/preferences.ts` | +| Faz-3 smoke | `scripts/i18n-phase3-smoke.*` | + +## Verification + +Calistirilan komutlar: + +```bash +pnpm typecheck +pnpm i18n:phase3-smoke +pnpm build +pnpm phase9:smoke +``` + +Sonuclar basarili. `pnpm build` sirasinda mevcut Next edge runtime static +generation uyarisi tekrar goruldu; Faz-3 kaynakli yeni bir hata degil. + +## Faz-4'e kalan sinir + +Bu faz ceviri yonetimini ekledi; uygulamadaki tum hardcoded metinleri henuz +katalog anahtarina tasimadi. Sayfa sayfa metin migration'i Faz-4'te yapilacak. diff --git a/docs/self-hosted-redesign/i18n-phase-4-hardcoded-text-report.md b/docs/self-hosted-redesign/i18n-phase-4-hardcoded-text-report.md new file mode 100644 index 0000000..717b65f --- /dev/null +++ b/docs/self-hosted-redesign/i18n-phase-4-hardcoded-text-report.md @@ -0,0 +1,265 @@ +--- +title: Faz 4 Kalan Hardcoded Metin Raporu +phase: 4 +status: generated +last_updated: 2026-07-18T23:56:57.608Z +--- + +# Faz 4 Kalan Hardcoded Metin Raporu + +Bu rapor Faz-4 boundary script'i tarafindan uretilir. Formatter sabitleri release blocker kabul edilir; kalan Turkce stringler Faz-4 kapsaminda raporlanir ve sonraki UI migration dalgalarinda eritilir. + +Toplam kalan Turkce literal satiri: 294 + +- `app/(dashboard)/analytics/analytics-client.tsx:56` +- `app/(dashboard)/business/subscriptions/subscriptions-client.tsx:38` return "Aylık"; +- `app/(dashboard)/business/subscriptions/subscriptions-client.tsx:40` return "Yıllık"; +- `app/(dashboard)/business/subscriptions/subscriptions-client.tsx:42` return "Haftalık"; +- `app/(dashboard)/calendar/actions.ts:17` title: requiredText(formData.get("title"), "Etkinlik başlığı zorunludur."), +- `app/(dashboard)/calendar/actions.ts:46` if (!value.startsAt) throw new Error("Etkinlik başlangıç zamanı zorunludur."); +- `app/(dashboard)/calendar/actions.ts:53` const id = requiredText(formData.get("id"), "Etkinlik kaydı bulunamadı."); +- `app/(dashboard)/calendar/actions.ts:55` if (!value.startsAt) throw new Error("Etkinlik başlangıç zamanı zorunludur."); +- `app/(dashboard)/calendar/actions.ts:64` requiredText(formData.get("id"), "Silinecek etkinlik bulunamadı."), +- `app/(dashboard)/calendar/calendar-client.tsx:55` meeting: "Toplantı", +- `app/(dashboard)/calendar/calendar-client.tsx:58` personal: "Kişisel", +- `app/(dashboard)/calendar/calendar-client.tsx:134` {["Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"].map((day) => ( +- `app/(dashboard)/calendar/calendar-client.tsx:245` {event.projectName || event.clientName || event.taskTitle || event.description || "Bağlantı yok"} +- `app/(dashboard)/calendar/calendar-client.tsx:293` toast.success(mode === "create" ? "Etkinlik eklendi." : "Etkinlik güncellendi."); +- `app/(dashboard)/calendar/calendar-client.tsx:298` : "Etkinlik kaydedilirken beklenmeyen bir hata oluştu.", +- `app/(dashboard)/calendar/calendar-client.tsx:310` {mode === "create" ? "Etkinlik ekle" : "Düzenle"} +- `app/(dashboard)/calendar/calendar-client.tsx:317` {mode === "create" ? "Yeni etkinlik" : "Etkinliği düzenle"} +- `app/(dashboard)/calendar/calendar-client.tsx:328` {isSubmitting ? "Kaydediliyor" : mode === "create" ? "Etkinliği ekle" : "Değişiklikleri kaydet"} +- `app/(dashboard)/calendar/calendar-client.tsx:356` +- `app/(dashboard)/calendar/calendar-client.tsx:363` +- `app/(dashboard)/calendar/calendar-client.tsx:370` +- `app/(dashboard)/calendar/calendar-client.tsx:380` +- `app/(dashboard)/calendar/calendar-client.tsx:404` +- `app/(dashboard)/chat/page.tsx:58` toast.error(error.message || "Yapay zeka ile iletişim kurulurken bir hata oluştu."); +- `app/(dashboard)/chat/page.tsx:74` toast.error(error instanceof Error ? error.message : "Sohbetler yüklenemedi."); +- `app/(dashboard)/chat/page.tsx:97` toast.error(error instanceof Error ? error.message : "Mesajlar yüklenemedi."); +- `app/(dashboard)/chat/page.tsx:143` toast.error(error instanceof Error ? error.message : "Sohbet oluşturulamadı."); +- `app/(dashboard)/chat/page.tsx:189` {session.title || "İsimsiz sohbet"} +- `app/(dashboard)/chat/page.tsx:196` aria-label={`${session.title || "İsimsiz sohbet"} sohbetini sil`} +- `app/(dashboard)/chat/page.tsx:302` placeholder="Mesaj gönder..." +- `app/(dashboard)/clients/[id]/actions.ts:19` title: requiredText(formData.get("title"), "Aktivite başlığı zorunludur."), +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:95` throw new Error(data.error || "Kullanıcı oluşturulamadı."); +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:99` toast.success("Güvenli portal daveti oluşturuldu."); +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:101` toast.error(error instanceof Error ? error.message : "Davet oluşturulamadı."); +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:116` if (!response.ok || data.error) throw new Error(data.error || "Portal dili güncellenemedi."); +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:117` toast.success("Portal dili güncellendi."); +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:120` toast.error(error instanceof Error ? error.message : "Portal dili güncellenemedi."); +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:165` +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:185` aria-label="Davet bağlantısını kopyala" +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:188` toast.success("Davet bağlantısı kopyalandı."); +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:310` +- `app/(dashboard)/clients/[id]/client-detail-client.tsx:318`