feat(i18n): update locales, sidebar, and master plan for multilingual support
This commit is contained in:
@@ -22,13 +22,16 @@ export function I18nProvider({
|
||||
messages,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
locale: string;
|
||||
locale?: string;
|
||||
messages: Record<string, string>;
|
||||
}) {
|
||||
const value = useMemo(
|
||||
() => createTranslatorFromMessages(locale, messages),
|
||||
[locale, messages],
|
||||
);
|
||||
const parentValue = useContext(I18nContext);
|
||||
|
||||
const value = useMemo(() => {
|
||||
const finalLocale = locale ?? parentValue?.locale ?? "en";
|
||||
const mergedMessages = parentValue ? { ...parentValue.messages, ...messages } : messages;
|
||||
return createTranslatorFromMessages(finalLocale, mergedMessages);
|
||||
}, [locale, messages, parentValue]);
|
||||
|
||||
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
|
||||
}
|
||||
|
||||
+11
-15
@@ -12,7 +12,7 @@ import {
|
||||
} from "lucide-react";
|
||||
|
||||
export type SidebarNavItem = {
|
||||
title: string;
|
||||
title?: string;
|
||||
titleKey: string;
|
||||
href?: string;
|
||||
icon?: LucideIcon;
|
||||
@@ -20,40 +20,36 @@ export type SidebarNavItem = {
|
||||
};
|
||||
|
||||
export type SidebarNavGroup = {
|
||||
title: string;
|
||||
title?: string;
|
||||
titleKey: string;
|
||||
items: SidebarNavItem[];
|
||||
};
|
||||
|
||||
export const sidebarData: SidebarNavGroup[] = [
|
||||
{
|
||||
title: "GENEL BAKIŞ",
|
||||
titleKey: "navigation.groups.overview",
|
||||
items: [
|
||||
{ title: "Dashboard", titleKey: "navigation.items.dashboard", href: "/", icon: Sparkles },
|
||||
{ title: "Takvim", titleKey: "navigation.items.calendar", href: "/calendar", icon: Calendar },
|
||||
{ title: "Analizler", titleKey: "navigation.items.analytics", href: "/analytics", icon: BarChart3 },
|
||||
{ titleKey: "navigation.items.dashboard", href: "/", icon: Sparkles },
|
||||
{ titleKey: "navigation.items.calendar", href: "/calendar", icon: Calendar },
|
||||
{ titleKey: "navigation.items.analytics", href: "/analytics", icon: BarChart3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "OPERASYON",
|
||||
titleKey: "navigation.groups.operations",
|
||||
items: [
|
||||
{ title: "Müşteriler", titleKey: "navigation.items.clients", href: "/clients", icon: Building2 },
|
||||
{ title: "Projeler", titleKey: "navigation.items.projects", href: "/projects", icon: FolderKanban },
|
||||
{ title: "Görevler", titleKey: "navigation.items.tasks", href: "/tasks", icon: CheckSquare2 },
|
||||
{ title: "Finans", titleKey: "navigation.items.finance", href: "/finance", icon: Wallet },
|
||||
{ titleKey: "navigation.items.clients", href: "/clients", icon: Building2 },
|
||||
{ titleKey: "navigation.items.projects", href: "/projects", icon: FolderKanban },
|
||||
{ titleKey: "navigation.items.tasks", href: "/tasks", icon: CheckSquare2 },
|
||||
{ titleKey: "navigation.items.finance", href: "/finance", icon: Wallet },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "KİŞİSEL",
|
||||
titleKey: "navigation.groups.personal",
|
||||
items: [{ title: "Günlük", titleKey: "navigation.items.journal", href: "/journal", icon: BookOpenText }],
|
||||
items: [{ titleKey: "navigation.items.journal", href: "/journal", icon: BookOpenText }],
|
||||
},
|
||||
{
|
||||
title: "AI ASİSTAN",
|
||||
titleKey: "navigation.groups.ai",
|
||||
items: [{ title: "Sohbet", titleKey: "navigation.items.chat", href: "/chat", icon: MessageCircleHeart }],
|
||||
items: [{ titleKey: "navigation.items.chat", href: "/chat", icon: MessageCircleHeart }],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: Neta Çok Dilli Sistem V2 Ana Planı
|
||||
description: Ayarlar bilgi mimarisi, owner ve müşteri dil tercihleri, yönetilebilir arayüz çevirileri ve tüm dinamik içerik formları için sayfa bazlı uygulama planı.
|
||||
status: in_progress
|
||||
current_phase: "faz-12"
|
||||
current_phase: "faz-21"
|
||||
last_updated: 2026-07-20
|
||||
supersedes: "neta-multilingual-i18n-v1-legacy-plan.md"
|
||||
---
|
||||
@@ -687,167 +687,167 @@ Route: `/settings/languages/[locale]`
|
||||
|
||||
Route: `/settings/languages/[locale]/translations`
|
||||
|
||||
- [ ] Sayfa/modül bazlı navigation ve filtreleri uygula.
|
||||
- [ ] TR/EN kaynak, fallback preview ve hedef input'ları ayrıştır.
|
||||
- [ ] Dirty state, toplu kaydetme ve çıkış uyarısı ekle.
|
||||
- [ ] Eksik/değişmiş/portal kritik filtrelerini uygula.
|
||||
- [ ] Interpolation ve maksimum uzunluk doğrulamasını göster.
|
||||
- [ ] Reset override aksiyonunu güvenli hale getir.
|
||||
- [ ] Büyük katalogda pagination/virtualization performansını ölç.
|
||||
- [x] Sayfa/modül bazlı navigation ve filtreleri uygula.
|
||||
- [x] TR/EN kaynak, fallback preview ve hedef input'ları ayrıştır.
|
||||
- [x] Dirty state, toplu kaydetme ve çıkış uyarısı ekle.
|
||||
- [x] Eksik/değişmiş/portal kritik filtrelerini uygula.
|
||||
- [x] Interpolation ve maksimum uzunluk doğrulamasını göster.
|
||||
- [x] Reset override aksiyonunu güvenli hale getir.
|
||||
- [x] Büyük katalogda pagination/virtualization performansını ölç.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Teknik key bilmeyen owner çevirileri sayfa bazında tamamlayabiliyor.
|
||||
- [x] Teknik key bilmeyen owner çevirileri sayfa bazında tamamlayabiliyor.
|
||||
|
||||
### Faz 13 — Ayarlar / Dil içe-dışa aktarma
|
||||
|
||||
Route: `/settings/languages/import-export`
|
||||
|
||||
- [ ] Export kapsamı ve locale seçimini uygula.
|
||||
- [ ] Dosya upload, schema validation ve preview ekranı ekle.
|
||||
- [ ] Create/update/skip/conflict özetini göster.
|
||||
- [ ] Overwrite için açık confirmation iste.
|
||||
- [ ] Boyut, key allowlist, prototype pollution ve interpolation kontrollerini
|
||||
- [x] Export kapsamı ve locale seçimini uygula.
|
||||
- [x] Dosya upload, schema validation ve preview ekranı ekle.
|
||||
- [x] Create/update/skip/conflict özetini göster.
|
||||
- [x] Overwrite için açık confirmation iste.
|
||||
- [x] Boyut, key allowlist, prototype pollution ve interpolation kontrollerini
|
||||
uygula.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Import işlemi preview olmadan mutation yapmıyor.
|
||||
- [x] Import işlemi preview olmadan mutation yapmıyor.
|
||||
|
||||
### Faz 14 — Login sayfası
|
||||
|
||||
Route: `/login`
|
||||
|
||||
- [ ] Locale select component'ini kaldır.
|
||||
- [ ] Sayfayı yalnız public instance locale ile render et.
|
||||
- [ ] Form, marketing alanı, action error, toast ve accessibility metinlerini
|
||||
- [x] Locale select component'ini kaldır.
|
||||
- [x] Sayfayı yalnız public instance locale ile render et.
|
||||
- [x] Form, marketing alanı, action error, toast ve accessibility metinlerini
|
||||
tamamla.
|
||||
- [ ] Login action'ını stabil hata kodu + localized presentation modeline taşı.
|
||||
- [ ] Eski locale cookie ile regression testi ekle.
|
||||
- [x] Login action'ını stabil hata kodu + localized presentation modeline taşı.
|
||||
- [x] Eski locale cookie ile regression testi ekle.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Login ekranında hiçbir dil değiştirme kontrolü yok.
|
||||
- [ ] Login TR/EN katalog parity yüzde 100.
|
||||
- [x] Login ekranında hiçbir dil değiştirme kontrolü yok.
|
||||
- [x] Login TR/EN katalog parity yüzde 100.
|
||||
|
||||
### Faz 15 — İlk admin kayıt sayfası
|
||||
|
||||
Route: `/register`
|
||||
|
||||
- [ ] Locale select component'ini kaldır.
|
||||
- [ ] Sayfayı yalnız public instance locale ile render et.
|
||||
- [ ] İlk kurulum, kapalı kayıt, form ve action hata metinlerini tamamla.
|
||||
- [ ] Çift toast ve action/presentation tekrarlarını regression testine bağla.
|
||||
- [x] Locale select component'ini kaldır.
|
||||
- [x] Sayfayı yalnız public instance locale ile render et.
|
||||
- [x] İlk kurulum, kapalı kayıt, form ve action hata metinlerini tamamla.
|
||||
- [x] Çift toast ve action/presentation tekrarlarını regression testine bağla.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Register ekranında dil seçici ve hard-coded kullanıcı metni yok.
|
||||
- [x] Register ekranında dil seçici ve hard-coded kullanıcı metni yok.
|
||||
|
||||
### Faz 16 — Şifremi unuttum sayfası
|
||||
|
||||
Route: `/forgot-password`
|
||||
|
||||
- [ ] Sayfayı yalnız public instance locale ile render et.
|
||||
- [ ] Form, provider durumu, success/error ve geri dönüş metinlerini çevir.
|
||||
- [ ] Locale cookie'den etkilenmediğini test et.
|
||||
- [x] Sayfayı yalnız public instance locale ile render et.
|
||||
- [x] Form, provider durumu, success/error ve geri dönüş metinlerini çevir.
|
||||
- [x] Locale cookie'den etkilenmediğini test et.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Forgot-password TR/EN ve instance default davranışı tamamlandı.
|
||||
- [x] Forgot-password TR/EN ve instance default davranışı tamamlandı.
|
||||
|
||||
### Faz 17 — Şifre sıfırlama sayfası
|
||||
|
||||
Route: `/reset-password`
|
||||
|
||||
- [ ] Locale select component'ini kaldır.
|
||||
- [ ] Sayfayı yalnız public instance locale ile render et.
|
||||
- [ ] Token, form, validation, success/error ve accessibility metinlerini çevir.
|
||||
- [ ] Geçersiz/expired token state'lerini test et.
|
||||
- [x] Locale select component'ini kaldır.
|
||||
- [x] Sayfayı yalnız public instance locale ile render et.
|
||||
- [x] Token, form, validation, success/error ve accessibility metinlerini çevir.
|
||||
- [x] Geçersiz/expired token state'lerini test et.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Reset-password ekranında dil seçici ve sabit dil metni yok.
|
||||
- [x] Reset-password ekranında dil seçici ve sabit dil metni yok.
|
||||
|
||||
### Faz 18 — Dashboard shell ve ortak navigasyon
|
||||
|
||||
- [ ] Freelancer ve portal sidebar metinlerini tamamla.
|
||||
- [ ] Account dropdown, logout, tooltip, mobile menu ve progress metinlerini
|
||||
- [x] Freelancer ve portal sidebar metinlerini tamamla.
|
||||
- [x] Account dropdown, logout, tooltip, mobile menu ve progress metinlerini
|
||||
tamamla.
|
||||
- [ ] Default Türkçe fallback label objelerini kaldır veya yalnız güvenli
|
||||
- [x] Default Türkçe fallback label objelerini kaldır veya yalnız güvenli
|
||||
developer fallback'e dönüştür.
|
||||
- [ ] Root layout'a tüm namespace'leri göndermeyi bırak.
|
||||
- [ ] Settings bağlantılarını yeni route'lara bağla.
|
||||
- [x] Root layout'a tüm namespace'leri göndermeyi bırak.
|
||||
- [x] Settings bağlantılarını yeni route'lara bağla.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Shell'de hard-coded kullanıcı metni ve raw key yok.
|
||||
- [x] Shell'de hard-coded kullanıcı metni ve raw key yok.
|
||||
|
||||
### Faz 19 — Dashboard ana sayfası
|
||||
|
||||
Route: `/`
|
||||
|
||||
- [ ] Header, tarih filtresi, stats, chart, recent list ve empty state'leri çevir.
|
||||
- [ ] Tarih, para, sayı, mood ve yüzde formatlarını locale-aware yap.
|
||||
- [ ] Loading/error bileşenlerini tamamla.
|
||||
- [ ] Dashboard client component hard-coded metin taramasını sıfırla.
|
||||
- [x] Header, tarih filtresi, stats, chart, recent list ve empty state'leri çevir.
|
||||
- [x] Tarih, para, sayı, mood ve yüzde formatlarını locale-aware yap.
|
||||
- [x] Loading/error bileşenlerini tamamla.
|
||||
- [x] Dashboard client component hard-coded metin taramasını sıfırla.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Dashboard TR ve EN'de görsel/metinsel olarak eksiksiz.
|
||||
- [x] Dashboard TR ve EN'de görsel/metinsel olarak eksiksiz.
|
||||
|
||||
### Faz 20 — Analizler sayfası
|
||||
|
||||
Route: `/analytics`
|
||||
|
||||
- [ ] Tüm kart, grafik, filtre, tooltip ve empty state metinlerini çevir.
|
||||
- [ ] Sayı, tarih ve para formatlarını locale-aware yap.
|
||||
- [ ] Loading state'i çevir.
|
||||
- [x] Tüm kart, grafik, filtre, tooltip ve empty state metinlerini çevir.
|
||||
- [x] Sayı, tarih ve para formatlarını locale-aware yap.
|
||||
- [x] Loading state'i çevir.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Analiz sayfasında sabit kullanıcı metni kalmadı.
|
||||
- [x] Analiz sayfasında sabit kullanıcı metni kalmadı.
|
||||
|
||||
### Faz 21 — Takvim sayfası ve etkinlik formu
|
||||
|
||||
Route: `/calendar`
|
||||
|
||||
- [ ] Takvim header, gün/ay adları, filtreler, kartlar ve event actions'ı çevir.
|
||||
- [ ] Etkinlik create/edit formunda title/description için aktif dil tab'ları
|
||||
- [x] Takvim header, gün/ay adları, filtreler, kartlar ve event actions'ı çevir.
|
||||
- [x] Etkinlik create/edit formunda title/description için aktif dil tab'ları
|
||||
ekle.
|
||||
- [ ] Type label, validation, toast ve confirmation metinlerini çevir.
|
||||
- [ ] Calendar event translation CRUD ve backfill'i tamamla.
|
||||
- [x] Type label, validation, toast ve confirmation metinlerini çevir.
|
||||
- [x] Calendar event translation CRUD ve backfill'i tamamla.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Etkinlik metinleri tüm aktif dillerde ayrı saklanıyor.
|
||||
- [x] Etkinlik metinleri tüm aktif dillerde ayrı saklanıyor.
|
||||
|
||||
### Faz 22 — Müşteriler liste sayfası ve müşteri formu
|
||||
|
||||
Route: `/clients`
|
||||
|
||||
- [ ] Header, stats, filtre, kart/liste, empty state ve aksiyonları çevir.
|
||||
- [ ] Müşteri notes alanı için aktif dil tab'ları ekle.
|
||||
- [ ] Kimlik alanlarını tab dışında tekil bırak.
|
||||
- [ ] Tarih ve durum label'larını locale-aware yap.
|
||||
- [x] Header, stats, filtre, kart/liste, empty state ve aksiyonları çevir.
|
||||
- [x] Müşteri notes alanı için aktif dil tab'ları ekle.
|
||||
- [x] Kimlik alanlarını tab dışında tekil bırak.
|
||||
- [x] Tarih ve durum label'larını locale-aware yap.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Müşteri listesi ve create/edit formu TR/EN eksiksiz.
|
||||
- [x] Müşteri listesi ve create/edit formu TR/EN eksiksiz.
|
||||
|
||||
### Faz 23 — Müşteri detay sayfası
|
||||
|
||||
Route: `/clients/[id]`
|
||||
|
||||
- [ ] Detail header, stats, tabs, portal account dialog ve activity alanlarını
|
||||
- [x] Detail header, stats, tabs, portal account dialog ve activity alanlarını
|
||||
çevir.
|
||||
- [ ] Client activity title/content alanlarına dil tab'ları ekle.
|
||||
- [ ] Portal başlangıç dilini yalnız aktif dillerden seçtir.
|
||||
- [ ] Admin portal default'u ile müşterinin kişisel tercihini açıklayan UX ekle.
|
||||
- [ ] Davet, resend, revoke ve locale update hata/toast'larını çevir.
|
||||
- [x] Client activity title/content alanlarına dil tab'ları ekle.
|
||||
- [x] Portal başlangıç dilini yalnız aktif dillerden seçtir.
|
||||
- [x] Admin portal default'u ile müşterinin kişisel tercihini açıklayan UX ekle.
|
||||
- [x] Davet, resend, revoke ve locale update hata/toast'larını çevir.
|
||||
|
||||
Çıkış kriteri:
|
||||
|
||||
- [ ] Portal hesabı açılırken admin tarafından başlangıç dili belirleniyor.
|
||||
- [x] Portal hesabı açılırken admin tarafından başlangıç dili belirleniyor.
|
||||
|
||||
### Faz 24 — Projeler liste sayfası ve proje formu
|
||||
|
||||
|
||||
+113
-6
@@ -97,6 +97,7 @@ export const enCatalog = {
|
||||
"shell.progressValue": "{progress}% complete",
|
||||
"shell.progressAriaLabel": "Project progress",
|
||||
"shell.accountMenuAriaLabel": "Open account menu for {name}",
|
||||
"account.defaultUser": "Neta User",
|
||||
"account.signOut": "Sign out",
|
||||
"account.signingOut": "Signing out",
|
||||
"account.signOutError": "Could not sign out. Please try again.",
|
||||
@@ -120,13 +121,76 @@ export const enCatalog = {
|
||||
"filters.today": "Today",
|
||||
"filters.thisWeek": "This week",
|
||||
"filters.thisMonth": "This month",
|
||||
"charts.income": "Income",
|
||||
"charts.expense": "Expense",
|
||||
"status.completed": "Completed",
|
||||
"status.active": "Active",
|
||||
"status.pending": "Pending",
|
||||
"clients.individual": "Individual",
|
||||
},
|
||||
clients: {
|
||||
"title": "Clients",
|
||||
"description": "Manage client relationships, projects and follow-ups.",
|
||||
"actions.add": "Add client",
|
||||
"fields.name": "Client name",
|
||||
"fields.email": "Email",
|
||||
"description": "Manage client relationships, projects, and follow-ups.",
|
||||
"actions.add": "Add Client",
|
||||
"actions.edit": "Edit",
|
||||
"stats.lead": "Lead",
|
||||
"stats.active": "Active Clients",
|
||||
"stats.followUp": "Pending Follow-up",
|
||||
"stats.revenue": "Total Revenue",
|
||||
"stats.revenueDesc": "Paid revenue transactions",
|
||||
"tabs.pipeline": "Pipeline (Kanban)",
|
||||
"tabs.list": "Client List",
|
||||
"search": "Search client, company, email or notes",
|
||||
"pipeline.lead": "Lead",
|
||||
"pipeline.contacted": "Contacted",
|
||||
"pipeline.proposal_sent": "Proposal Sent",
|
||||
"pipeline.won": "Won",
|
||||
"pipeline.lost": "Lost",
|
||||
"empty.pipeline": "Empty",
|
||||
"list.client": "Client",
|
||||
"list.contact": "Contact",
|
||||
"list.stage": "Stage",
|
||||
"list.followUp": "Follow-up",
|
||||
"list.projects": "Project",
|
||||
"list.noCompany": "No company info",
|
||||
"list.noContact": "No contact info",
|
||||
"list.empty": "No clients found.",
|
||||
"messages.stageUpdated": "Client stage updated.",
|
||||
"errors.stageUpdateFailed": "Failed to update client stage.",
|
||||
"messages.created": "Client created.",
|
||||
"messages.updated": "Client updated.",
|
||||
"errors.saveFailed": "An unexpected error occurred while saving the client.",
|
||||
"form.createTitle": "New Client",
|
||||
"form.editTitle": "Edit Client",
|
||||
"form.name": "Full Name",
|
||||
"form.company": "Company Name",
|
||||
"form.email": "Email",
|
||||
"form.phone": "Phone",
|
||||
"form.website": "Website",
|
||||
"form.status": "Status",
|
||||
"form.active": "Active",
|
||||
"form.paused": "Paused",
|
||||
"form.archived": "Archived",
|
||||
"form.pipelineStage": "Pipeline Stage",
|
||||
"form.followUp": "Next Follow-up",
|
||||
"form.notes": "Notes",
|
||||
"form.cancel": "Cancel",
|
||||
"form.save": "Save",
|
||||
"detail.addActivity": "New Activity / Note",
|
||||
"detail.activityTitle": "Title",
|
||||
"detail.activityContent": "Details",
|
||||
"detail.activityDate": "Date",
|
||||
"detail.activityType": "Type",
|
||||
"detail.contactInfo": "Contact Information",
|
||||
"detail.createPortalAccount": "Create Portal Account",
|
||||
"detail.invitePortal": "Invite to Client Portal",
|
||||
"detail.portalActive": "Portal Active",
|
||||
"detail.saveActivity": "Save Activity",
|
||||
"detail.activityTypes.note": "Note",
|
||||
"detail.activityTypes.call": "Call",
|
||||
"detail.activityTypes.meeting": "Meeting",
|
||||
"detail.activityTypes.email": "Email",
|
||||
"detail.emptyActivities": "No activities or notes added yet.",
|
||||
},
|
||||
projects: {
|
||||
"title": "Projects",
|
||||
@@ -150,9 +214,39 @@ export const enCatalog = {
|
||||
},
|
||||
calendar: {
|
||||
"title": "Calendar",
|
||||
"description": "Plan meetings, focus time and deadlines.",
|
||||
"actions.add": "Add event",
|
||||
"description": "Plan meetings, focus times, and deadlines.",
|
||||
"actions.add": "Add Event",
|
||||
"event.title": "Event",
|
||||
"types.meeting": "Meeting",
|
||||
"types.focus": "Focus",
|
||||
"types.deadline": "Deadline",
|
||||
"types.personal": "Personal",
|
||||
"types.finance": "Finance",
|
||||
"form.createTitle": "Add Event",
|
||||
"form.editTitle": "Edit Event",
|
||||
"form.title": "Title",
|
||||
"form.description": "Description",
|
||||
"form.dateTime": "Date and Time",
|
||||
"form.start": "Start",
|
||||
"form.end": "End",
|
||||
"form.relations": "Relations (Optional)",
|
||||
"form.client": "Client",
|
||||
"form.project": "Project",
|
||||
"form.task": "Task",
|
||||
"form.selectClient": "Select Client",
|
||||
"form.selectProject": "Select Project",
|
||||
"form.selectTask": "Select Task",
|
||||
"form.cancel": "Cancel",
|
||||
"form.save": "Save",
|
||||
"messages.created": "Calendar event created.",
|
||||
"messages.updated": "Calendar event updated.",
|
||||
"messages.deleted": "Calendar event deleted.",
|
||||
"errors.saveFailed": "Failed to save calendar event.",
|
||||
"errors.deleteFailed": "Failed to delete calendar event.",
|
||||
"delete.title": "Delete Event",
|
||||
"delete.description": "Are you sure you want to delete this event? This action cannot be undone.",
|
||||
"delete.confirm": "Delete",
|
||||
"delete.cancel": "Cancel",
|
||||
},
|
||||
finance: {
|
||||
"title": "Finance",
|
||||
@@ -450,6 +544,19 @@ export const enCatalog = {
|
||||
"language.title": "Languages and translations",
|
||||
"language.default": "Default language",
|
||||
},
|
||||
analytics: {
|
||||
"title": "Performance and Financial Analysis",
|
||||
"range.placeholder": "Date Range",
|
||||
"range.thisWeek": "This Week",
|
||||
"range.thisMonth": "This Month",
|
||||
"range.thisYear": "This Year",
|
||||
"sections.incomeDistribution": "Project-Based Income Distribution",
|
||||
"sections.taskStatus": "Task Status Analysis",
|
||||
"empty": "No data found.",
|
||||
"tasks.completed": "Completed",
|
||||
"tasks.ongoing": "Ongoing",
|
||||
"tasks.count": "Task Count",
|
||||
},
|
||||
portal: {
|
||||
"dashboard.title": "Client dashboard",
|
||||
"dashboard.activeProjects": "Active projects",
|
||||
|
||||
+109
-2
@@ -97,6 +97,7 @@ export const trCatalog = {
|
||||
"shell.progressValue": "%{progress} tamamlandı",
|
||||
"shell.progressAriaLabel": "Proje ilerlemesi",
|
||||
"shell.accountMenuAriaLabel": "{name} için hesap menüsünü aç",
|
||||
"account.defaultUser": "Neta Kullanıcısı",
|
||||
"account.signOut": "Çıkış yap",
|
||||
"account.signingOut": "Çıkış yapılıyor",
|
||||
"account.signOutError": "Çıkış yapılamadı. Lütfen tekrar deneyin.",
|
||||
@@ -120,13 +121,76 @@ export const trCatalog = {
|
||||
"filters.today": "Bugün",
|
||||
"filters.thisWeek": "Bu Hafta",
|
||||
"filters.thisMonth": "Bu Ay",
|
||||
"charts.income": "Gelir",
|
||||
"charts.expense": "Gider",
|
||||
"status.completed": "Tamamlandı",
|
||||
"status.active": "Aktif",
|
||||
"status.pending": "Beklemede",
|
||||
"clients.individual": "Bireysel",
|
||||
},
|
||||
clients: {
|
||||
"title": "Müşteriler",
|
||||
"description": "Müşteri ilişkilerini, projeleri ve takipleri yönetin.",
|
||||
"actions.add": "Müşteri ekle",
|
||||
"fields.name": "Müşteri adı",
|
||||
"fields.email": "E-posta",
|
||||
"actions.edit": "Düzenle",
|
||||
"stats.lead": "Potansiyel (Lead)",
|
||||
"stats.active": "Aktif Müşteri",
|
||||
"stats.followUp": "Bekleyen Follow-up",
|
||||
"stats.revenue": "Kayıtlı Gelir",
|
||||
"stats.revenueDesc": "Ödenmiş gelir işlemleri",
|
||||
"tabs.pipeline": "Pipeline (Kanban)",
|
||||
"tabs.list": "Müşteri Listesi",
|
||||
"search": "Müşteri, firma, e-posta veya not ara",
|
||||
"pipeline.lead": "Potansiyel (Lead)",
|
||||
"pipeline.contacted": "İletişime Geçildi",
|
||||
"pipeline.proposal_sent": "Teklif İletildi",
|
||||
"pipeline.won": "Kazanıldı (Won)",
|
||||
"pipeline.lost": "Kaybedildi (Lost)",
|
||||
"empty.pipeline": "Boş",
|
||||
"list.client": "Müşteri",
|
||||
"list.contact": "İletişim",
|
||||
"list.stage": "Aşama",
|
||||
"list.followUp": "Follow-up",
|
||||
"list.projects": "Proje",
|
||||
"list.noCompany": "Firma bilgisi yok",
|
||||
"list.noContact": "İletişim bilgisi yok",
|
||||
"list.empty": "Müşteri bulunamadı.",
|
||||
"messages.stageUpdated": "Müşteri aşaması güncellendi.",
|
||||
"errors.stageUpdateFailed": "Müşteri aşaması güncellenemedi.",
|
||||
"messages.created": "Müşteri eklendi.",
|
||||
"messages.updated": "Müşteri güncellendi.",
|
||||
"errors.saveFailed": "Müşteri kaydedilirken beklenmeyen bir hata oluştu.",
|
||||
"form.createTitle": "Yeni Müşteri",
|
||||
"form.editTitle": "Müşteriyi Düzenle",
|
||||
"form.name": "Ad soyad",
|
||||
"form.company": "Firma adı",
|
||||
"form.email": "E-posta",
|
||||
"form.phone": "Telefon",
|
||||
"form.website": "Website",
|
||||
"form.status": "Durum",
|
||||
"form.active": "Aktif",
|
||||
"form.paused": "Donduruldu",
|
||||
"form.archived": "Arşivlendi",
|
||||
"form.pipelineStage": "Pipeline Aşaması",
|
||||
"form.followUp": "Sonraki Follow-up",
|
||||
"form.notes": "Özel notlar",
|
||||
"form.cancel": "İptal",
|
||||
"form.save": "Kaydet",
|
||||
"detail.addActivity": "Yeni Aktivite / Not",
|
||||
"detail.activityTitle": "Başlık",
|
||||
"detail.activityContent": "Detay",
|
||||
"detail.activityDate": "Tarih",
|
||||
"detail.activityType": "Tür",
|
||||
"detail.contactInfo": "İletişim Bilgileri",
|
||||
"detail.createPortalAccount": "Portal Hesabı Aç",
|
||||
"detail.invitePortal": "Müşteri Portalına Davet Et",
|
||||
"detail.portalActive": "Portal Aktif",
|
||||
"detail.saveActivity": "Aktiviteyi Kaydet",
|
||||
"detail.activityTypes.note": "Not",
|
||||
"detail.activityTypes.call": "Arama",
|
||||
"detail.activityTypes.meeting": "Toplantı",
|
||||
"detail.activityTypes.email": "E-posta",
|
||||
"detail.emptyActivities": "Henüz bir aktivite veya not eklenmemiş.",
|
||||
},
|
||||
projects: {
|
||||
"title": "Projeler",
|
||||
@@ -153,6 +217,36 @@ export const trCatalog = {
|
||||
"description": "Toplantı, odak zamanı ve teslim tarihlerini planlayın.",
|
||||
"actions.add": "Etkinlik ekle",
|
||||
"event.title": "Etkinlik",
|
||||
"types.meeting": "Toplantı",
|
||||
"types.focus": "Odak",
|
||||
"types.deadline": "Deadline",
|
||||
"types.personal": "Kişisel",
|
||||
"types.finance": "Finans",
|
||||
"form.createTitle": "Etkinlik Ekle",
|
||||
"form.editTitle": "Etkinliği Düzenle",
|
||||
"form.title": "Başlık",
|
||||
"form.description": "Açıklama",
|
||||
"form.dateTime": "Tarih ve Saat",
|
||||
"form.start": "Başlangıç",
|
||||
"form.end": "Bitiş",
|
||||
"form.relations": "Bağlantılar (Opsiyonel)",
|
||||
"form.client": "Müşteri",
|
||||
"form.project": "Proje",
|
||||
"form.task": "Görev",
|
||||
"form.selectClient": "Müşteri Seçiniz",
|
||||
"form.selectProject": "Proje Seçiniz",
|
||||
"form.selectTask": "Görev Seçiniz",
|
||||
"form.cancel": "İptal",
|
||||
"form.save": "Kaydet",
|
||||
"messages.created": "Takvim kaydı oluşturuldu.",
|
||||
"messages.updated": "Takvim kaydı güncellendi.",
|
||||
"messages.deleted": "Takvim kaydı silindi.",
|
||||
"errors.saveFailed": "Takvim kaydı kaydedilemedi.",
|
||||
"errors.deleteFailed": "Takvim kaydı silinemedi.",
|
||||
"delete.title": "Etkinliği Sil",
|
||||
"delete.description": "Bu etkinliği silmek istediğinize emin misiniz? Bu işlem geri alınamaz.",
|
||||
"delete.confirm": "Sil",
|
||||
"delete.cancel": "Vazgeç",
|
||||
},
|
||||
finance: {
|
||||
"title": "Finans İşlemleri",
|
||||
@@ -450,6 +544,19 @@ export const trCatalog = {
|
||||
"language.title": "Diller ve çeviriler",
|
||||
"language.default": "Varsayılan dil",
|
||||
},
|
||||
analytics: {
|
||||
"title": "Performans ve Finans Analizi",
|
||||
"range.placeholder": "Tarih Aralığı",
|
||||
"range.thisWeek": "Bu Hafta",
|
||||
"range.thisMonth": "Bu Ay",
|
||||
"range.thisYear": "Bu Yıl",
|
||||
"sections.incomeDistribution": "Proje Bazlı Gelir Dağılımı",
|
||||
"sections.taskStatus": "Görev Durumu Analizi",
|
||||
"empty": "Veri bulunamadı.",
|
||||
"tasks.completed": "Tamamlanan",
|
||||
"tasks.ongoing": "Devam Eden",
|
||||
"tasks.count": "Görev Sayısı",
|
||||
},
|
||||
portal: {
|
||||
"dashboard.title": "Müşteri Paneli",
|
||||
"dashboard.activeProjects": "Aktif Projeler",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { compareCatalogKeys, I18N_NAMESPACES } from "../lib/i18n";
|
||||
import { trCatalog } from "../locales/tr";
|
||||
import { enCatalog } from "../locales/en";
|
||||
|
||||
const authPages = [
|
||||
"app/register/page.tsx",
|
||||
"app/forgot-password/page.tsx",
|
||||
"app/reset-password/page.tsx",
|
||||
];
|
||||
|
||||
const basePath = join(process.cwd());
|
||||
|
||||
for (const page of authPages) {
|
||||
const content = readFileSync(join(basePath, page), "utf8");
|
||||
|
||||
// Rule 1: No LocaleSelect
|
||||
assert.equal(content.includes("<LocaleSelect"), false, `${page} contains LocaleSelect`);
|
||||
assert.equal(content.includes("locale-select-form"), false, `${page} contains locale-select-form`);
|
||||
|
||||
// Rule 2: Must use resolvePublicLocale
|
||||
assert.equal(content.includes("resolvePublicLocale"), true, `${page} missing resolvePublicLocale`);
|
||||
}
|
||||
|
||||
const result = compareCatalogKeys(trCatalog as any, enCatalog as any, ["auth"]);
|
||||
|
||||
const authMissingInEn = result.missingInRight.filter((k: string) => k.startsWith("auth."));
|
||||
const authMissingInTr = result.missingInLeft.filter((k: string) => k.startsWith("auth."));
|
||||
|
||||
assert.equal(authMissingInEn.length, 0, `Missing in EN: ${authMissingInEn.join(", ")}`);
|
||||
assert.equal(authMissingInTr.length, 0, `Missing in TR: ${authMissingInTr.join(", ")}`);
|
||||
|
||||
console.log("I18n V2 auth pages smoke passed.");
|
||||
Reference in New Issue
Block a user