chore: add i18n release hardening gates
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
---
|
||||
title: I18n Faz 9 — Hardening, Migrasyon ve Release Readiness
|
||||
description: Çok dilli sistemin self-host production yayınına hazır olduğunu gösteren migration, backup, API, Dokploy ve operasyon raporu.
|
||||
phase: 9
|
||||
status: completed
|
||||
last_updated: 2026-07-19
|
||||
---
|
||||
|
||||
# I18n Faz 9 — Hardening, migrasyon ve release readiness
|
||||
|
||||
Faz-9, çok dilli sistemin production self-host kurulumunda güvenli şekilde
|
||||
yayınlanabilmesi için migration, backup/restore, import, mobil API ve operasyon
|
||||
kapılarını kapatır.
|
||||
|
||||
## Sonuç
|
||||
|
||||
Çok dilli sistem release adayıdır.
|
||||
|
||||
- Yeni kurulumda `tr` ve `en` aktif gelir, default locale `tr` olur.
|
||||
- Eski Türkçe kolonlar `content_translations` içine `tr` locale'iyle backfill
|
||||
edilir.
|
||||
- Backfill tekrar çalışan migration'da duplicate üretmez.
|
||||
- `fr` custom locale, content translation, backup ve restore zincirinde korunur.
|
||||
- API v1 localization sözleşmesi additive ve eski mobil client'larla uyumludur.
|
||||
- Runtime Supabase bağımlılığı veya Supabase env kullanımı geri gelmemiştir.
|
||||
- Built-in kataloglar standalone server bundle içinde bulunur.
|
||||
|
||||
## Release kapıları
|
||||
|
||||
### Migration
|
||||
|
||||
`pnpm i18n:phase9-hardening` şu migration senaryolarını gerçek temp SQLite DB
|
||||
üzerinde çalıştırır:
|
||||
|
||||
1. Boş DB migration.
|
||||
2. Boş DB üzerinde ikinci migration çalıştırması.
|
||||
3. 0000–0007 arası eski schema üzerinde production-benzeri Türkçe veri.
|
||||
4. 0008 i18n migration'ı sonrası Türkçe backfill doğrulaması.
|
||||
5. Aynı DB üzerinde tekrar migration ve idempotent count kontrolü.
|
||||
|
||||
Backfill doğrulanan alanlar:
|
||||
|
||||
- `project.name`
|
||||
- `project.description`
|
||||
- `task.title`
|
||||
- `planning_section.title`
|
||||
- `branding.portalWelcome`
|
||||
|
||||
### Backup ve restore
|
||||
|
||||
Hardening smoke, production-benzeri DB'ye `fr` custom locale ve Fransızca proje
|
||||
çevirisi ekler, backup alır ve ayrı bir data directory içine restore eder.
|
||||
Restore sonrası:
|
||||
|
||||
- `instance_locales.fr` aktif kalır.
|
||||
- `content_translations` içindeki Fransızca çeviri korunur.
|
||||
- `foreign_key_check` temiz döner.
|
||||
|
||||
Restore failure prosedürü de test edilir: checksum'u bozulan backup restore
|
||||
edilmez ve mevcut hedef DB korunur.
|
||||
|
||||
### Supabase import uyumu
|
||||
|
||||
Faz-9, Supabase import aracının Faz-8 smoke kapsamını release blocker kabul eder.
|
||||
`phase8:import-smoke` şu davranışları doğrular:
|
||||
|
||||
- dry-run DB'yi değiştirmez;
|
||||
- apply import kayıtları taşır;
|
||||
- `--allow-existing` idempotent upsert yapar;
|
||||
- invalid enum, eksik foreign key ve unsafe storage path reddedilir;
|
||||
- pre-import backup restore ile rollback provası yapılır.
|
||||
|
||||
Import aracı Supabase SDK kullanmaz; offline `neta-supabase-export` bundle'ını
|
||||
okur.
|
||||
|
||||
## Dokploy / Docker operasyon notu
|
||||
|
||||
Dokploy'da `/app/data` kalıcı volume olmalıdır. Bu volume şunları taşır:
|
||||
|
||||
- `neta.db`
|
||||
- `uploads/`
|
||||
- `backups/`
|
||||
- `tmp/`
|
||||
|
||||
Production deploy öncesi:
|
||||
|
||||
```bash
|
||||
pnpm db:backup -- --retention-count 14
|
||||
```
|
||||
|
||||
Restore provası production volume üstüne değil, ayrı geçici hedefe yapılmalıdır:
|
||||
|
||||
```bash
|
||||
pnpm db:restore -- \
|
||||
--from /app/data/backups/neta-TIMESTAMP \
|
||||
--target /tmp/neta-restore-rehearsal \
|
||||
--force
|
||||
```
|
||||
|
||||
Translation tabloları `neta.db` içinde olduğu için backup/restore kapsamında
|
||||
otomatik korunur:
|
||||
|
||||
- `instance_locales`
|
||||
- `instance_i18n_settings`
|
||||
- `instance_ui_translations`
|
||||
- `content_translations`
|
||||
- `clients.portal_locale`
|
||||
- `portal_invitations.locale`
|
||||
- `user_preferences.language`
|
||||
|
||||
## Self-host default locale
|
||||
|
||||
Yeni kurulumda default locale `tr` olur. Owner Ayarlar üzerinden varsayılan dili
|
||||
aktif başka bir locale'e alabilir. Default locale yalnız `active` locale olabilir;
|
||||
draft veya archived locale default yapılamaz.
|
||||
|
||||
Mobil istemciler instance dil bilgisini şu endpoint'lerden keşfeder:
|
||||
|
||||
- `GET /.well-known/neta`
|
||||
- `GET /api/v1/meta`
|
||||
- `GET /api/v1/me`
|
||||
|
||||
## Dil ekleme ve import/export rehberi
|
||||
|
||||
Owner akışı:
|
||||
|
||||
1. Ayarlar → Diller ve çeviriler bölümünden yeni locale ekle.
|
||||
2. Locale'i önce `draft` olarak tut.
|
||||
3. UI çeviri eksiklerini tamamla veya JSON import ile yükle.
|
||||
4. İçerik formlarındaki locale tab'larında proje/görev/portal metinlerini gir.
|
||||
5. Portal'a hazır olduğunda locale'i `active` yap.
|
||||
6. Gerekirse varsayılan locale'i değiştir.
|
||||
|
||||
Import/export:
|
||||
|
||||
- UI çeviri export paketi `neta-i18n` formatındadır.
|
||||
- Import yalnız owner scope ile çalışır.
|
||||
- Built-in locale'ler silinmez veya archived yapılamaz.
|
||||
- Bozuk namespace/key/value import'u validation ile reddedilir.
|
||||
|
||||
## Portal müşterisine dil atama
|
||||
|
||||
Müşteri portal dili iki noktada belirlenir:
|
||||
|
||||
1. Portal daveti oluştururken seçilen locale.
|
||||
2. Müşteri detayındaki portal dili ayarı.
|
||||
|
||||
Davet kabul edildiğinde:
|
||||
|
||||
- `clients.portal_locale` set edilir;
|
||||
- client user preference dili aynı locale'e alınır;
|
||||
- davet kabulü sonrası login ekranı davet locale'iyle açılır.
|
||||
|
||||
## Mobil API localization sözleşmesi
|
||||
|
||||
Mobil taraf locale'i şu sırayla çözmelidir:
|
||||
|
||||
1. Explicit query: `/api/v1/me?locale=en`
|
||||
2. Better Auth session preference
|
||||
3. Client portal locale
|
||||
4. `Accept-Language`
|
||||
5. Instance default locale
|
||||
|
||||
Unsupported explicit locale için server:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "UNSUPPORTED_LOCALE",
|
||||
"message": "Unsupported locale.",
|
||||
"details": {
|
||||
"messageKey": "validation.unsupportedLocale"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Client kullanıcıya gösterilecek metni kendi catalog'undan `messageKey` ile
|
||||
çözmelidir; program akışı stabil `code` alanına bağlanmalıdır.
|
||||
|
||||
## Gözlem ve performans
|
||||
|
||||
Faz-9 smoke seviyesi performans kapıları:
|
||||
|
||||
- Dashboard ve portal listeleri batch content resolver kullanır; N+1 translation
|
||||
sorgusu release riski kabul edilir.
|
||||
- Missing translation durumunda raw key production kullanıcı arayüzüne düşmemeli;
|
||||
fallback chain kullanılmalıdır.
|
||||
- Catalog version, UI translation mutation'larında artar ve mobil meta cache
|
||||
invalidation için kullanılabilir.
|
||||
- Hassas içerik loglanmamalıdır; missing translation log'u key/locale seviyesinde
|
||||
kalmalıdır.
|
||||
|
||||
## Verification
|
||||
|
||||
Çalıştırılan komutlar:
|
||||
|
||||
```bash
|
||||
pnpm phase-i18n:boundary
|
||||
pnpm i18n:phase9-hardening
|
||||
pnpm i18n:phase8-smoke
|
||||
pnpm i18n:phase7-smoke
|
||||
pnpm i18n:phase5-smoke
|
||||
pnpm i18n:phase3-smoke
|
||||
pnpm i18n:phase2-smoke
|
||||
pnpm i18n:phase1-smoke
|
||||
pnpm phase8:import-smoke
|
||||
pnpm typecheck
|
||||
pnpm build
|
||||
pnpm phase9:smoke
|
||||
git diff --check
|
||||
```
|
||||
|
||||
`phase9:smoke` local listener açtığı için sandbox içinde `EPERM` alabilir; bu
|
||||
durumda izinli çalıştırılmalıdır.
|
||||
|
||||
## Release uyarısı
|
||||
|
||||
Bu rapor teknik release readiness sağlar. Gerçek production cutover için ortam
|
||||
sahibi yine şu dış adımları yürütmelidir:
|
||||
|
||||
- production backup;
|
||||
- maintenance/read-only penceresi;
|
||||
- final Supabase export/import;
|
||||
- gerçek satır ve dosya checksum karşılaştırması;
|
||||
- owner/client kabul smoke'u;
|
||||
- DNS/reverse proxy geçişi;
|
||||
- rollback retention penceresi.
|
||||
Reference in New Issue
Block a user