- Statistiques de Traduction
+ Statistiques
- Analyse des traductions et patterns d'utilisation
+ Revenus, MRR, traductions et patterns d'utilisation
@@ -74,6 +75,8 @@ export default function StatsPage() {
)}
+
+
diff --git a/frontend/src/app/admin/types.ts b/frontend/src/app/admin/types.ts
index 263f60f..e33b77f 100644
--- a/frontend/src/app/admin/types.ts
+++ b/frontend/src/app/admin/types.ts
@@ -78,3 +78,90 @@ export interface TranslationStatsResponse {
generated_at: string;
};
}
+
+/* ── Modèles & abonnements (paliers IA) ─────────────────────── */
+
+export interface AiTierSettings {
+ /** Modèles actifs — l'ordre est la priorité de secours. */
+ models: string[];
+ /** Modèle par défaut ; vide = premier de la liste = défaut du plan. */
+ default_model: string;
+}
+
+export interface AiTiersSettings {
+ essential: AiTierSettings;
+ premium: AiTierSettings;
+}
+
+/* ── Statistiques enrichies (revenus, MRR, liste d'attente) ── */
+
+export interface AdminStatsResponse {
+ users: {
+ total: number;
+ active_this_month: number;
+ by_plan: Record;
+ };
+ translations: {
+ docs_this_month: number;
+ pages_this_month: number;
+ };
+ revenue: {
+ collected_total: number;
+ collected_30d: number;
+ credits_purchased: number;
+ payments_30d: number;
+ currency: string;
+ };
+ mrr_estimated: {
+ total: number;
+ by_plan: Record;
+ };
+ ai_tier_usage: {
+ essential: number;
+ premium: number;
+ classic: number;
+ other: number;
+ };
+ waitlist_count: number;
+ cache: Record;
+ config: Record;
+}
+
+/* ── Marketing (relances email) ─────────────────────────────── */
+
+export interface MarketingAudiences {
+ audiences: Record;
+ unsubscribed_count: number;
+}
+
+export interface MarketingAudiencesResponse {
+ data: MarketingAudiences;
+ meta: Record;
+}
+
+export interface MarketingEmailHistoryEntry {
+ id: string;
+ sent_at: string;
+ completed_at?: string;
+ audience: string;
+ subject: string;
+ test_mode: boolean;
+ test_recipient?: string;
+ recipients_total: number;
+ sent_count: number;
+ failed_count: number;
+ failed: { email: string; error: string }[];
+ content_hash?: string;
+}
+
+export interface MarketingEmailSendResponse {
+ data: {
+ test_mode: boolean;
+ sent?: number;
+ failed?: number;
+ recipient?: string;
+ queued?: number;
+ audience?: string;
+ };
+ meta: Record;
+}
diff --git a/frontend/src/app/pricing/page.tsx b/frontend/src/app/pricing/page.tsx
index a6d4c3c..d57da4a 100644
--- a/frontend/src/app/pricing/page.tsx
+++ b/frontend/src/app/pricing/page.tsx
@@ -922,7 +922,7 @@ export default function PricingPage() {
{t('pricing.aiModels.essential.context')}
- $0.25/$0.38 per 1M
+ {t('pricing.aiModels.essential.price')}
{t('pricing.aiModels.essential.value')}
@@ -933,11 +933,11 @@ export default function PricingPage() {
{t('pricing.aiModels.premium.plan')}
- {t('pricing.aiModels.premium.descPrefix')} Claude Sonnet 4.6 {t('pricing.aiModels.premium.descSuffix')}
+ {t('pricing.aiModels.premium.descPrefix')} {t('pricing.aiModels.premium.modelName')} {t('pricing.aiModels.premium.descSuffix')}
{t('pricing.aiModels.premium.context')}
- $3.00/$15.00 per 1M
+ {t('pricing.aiModels.premium.alternatives')}
{t('pricing.aiModels.premium.precision')}
diff --git a/frontend/src/lib/i18n/messages/ar/admin.json b/frontend/src/lib/i18n/messages/ar/admin.json
index d1846c4..c9b830a 100644
--- a/frontend/src/lib/i18n/messages/ar/admin.json
+++ b/frontend/src/lib/i18n/messages/ar/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "المزوّدون",
"admin.nav.system": "النظام",
"admin.nav.logs": "السجلات",
+ "admin.nav.stats": "الإحصائيات",
"admin.users.title": "إدارة المستخدمين",
"admin.users.subtitle": "عرض حسابات المستخدمين وإدارتها",
"admin.users.planUpdated": "تم تحديث الخطة",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "في انتظار البيانات...",
"admin.system.purging": "جارٍ الحذف...",
"admin.system.clean": "تنظيف",
- "admin.system.purge": "حذف"
+ "admin.system.purge": "حذف",
+ "admin.nav.models": "النماذج والاشتراكات",
+ "admin.nav.marketing": "التسويق",
+ "admin.marketing.audience": "الجمهور",
+ "admin.marketing.audienceAllUsers": "جميع الحسابات",
+ "admin.marketing.audienceInactive": "حسابات غير نشطة منذ 30 يومًا",
+ "admin.marketing.audiencePlaceholder": "اختر جمهورًا",
+ "admin.marketing.audiencePlanBusiness": "خطة Business",
+ "admin.marketing.audiencePlanEnterprise": "خطة Enterprise",
+ "admin.marketing.audiencePlanFree": "الخطة المجانية",
+ "admin.marketing.audiencePlanPro": "خطة Pro",
+ "admin.marketing.audiencePlanStarter": "خطة Starter",
+ "admin.marketing.audienceWaitlist": "قائمة الانتظار",
+ "admin.marketing.badgeReal": "فعلي",
+ "admin.marketing.badgeTest": "اختباري",
+ "admin.marketing.confirmSend": "إرسال هذه الرسالة إلى {count} مستلم («{audience}»)؟",
+ "admin.marketing.footerNote": "يُضاف تلقائيًا تذييل يحتوي على رابط إلغاء الاشتراك عند الإرسال.",
+ "admin.marketing.hidePreview": "إخفاء المعاينة",
+ "admin.marketing.history": "سجل الإرسالات",
+ "admin.marketing.historyFailedSuffix": "، {count} فاشلة",
+ "admin.marketing.historySent": "{sent}/{total} مُرسلة",
+ "admin.marketing.html": "محتوى HTML",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "أدخل الموضوع وHTML.",
+ "admin.marketing.incompleteTitle": "محتوى غير مكتمل",
+ "admin.marketing.loadErrorTitle": "خطأ في التحميل",
+ "admin.marketing.localCheckUnavailable": "التحقق المحلي غير متاح في هذا المتصفح: سيتحقق الخادم من الإرسال الاختباري المسبق.",
+ "admin.marketing.networkDesc": "لا يمكن الوصول إلى الخادم الخلفي.",
+ "admin.marketing.newCampaign": "حملة جديدة",
+ "admin.marketing.noHistory": "لا توجد إرسالات مسجلة.",
+ "admin.marketing.preview": "معاينة",
+ "admin.marketing.previewEmpty": "معاينة: الصق HTML بجانب هذه اللوحة.",
+ "admin.marketing.previewTitle": "معاينة البريد",
+ "admin.marketing.recipientsAvailable": "{count} مستلم متاح بعد استثناء الملغين لاشتراكهم.",
+ "admin.marketing.sendReal": "الإرسال إلى {count} مستلم",
+ "admin.marketing.sendRefusedTitle": "تم رفض الإرسال",
+ "admin.marketing.sendTest": "إرسال اختباري",
+ "admin.marketing.sendingDesc": "{count} رسالة في قائمة الإرسال (فاصل 0,2 ثانية). الملغون لاشتراكهم مستثنون.",
+ "admin.marketing.sendingTitle": "الإرسال جارٍ",
+ "admin.marketing.subject": "الموضوع",
+ "admin.marketing.subjectPlaceholder": "مثال: ترجمتك بانتظارك — خصم 20٪ لمدة 7 أيام",
+ "admin.marketing.subtitle": "إرسال رسائل إلى جمهور محدد، مع إرسال اختباري إلزامي ورابط إلغاء اشتراك تلقائي وسجل كامل.",
+ "admin.marketing.testDoneDesc": "وصل البريد إلى {email}",
+ "admin.marketing.testDoneTitle": "تم الإرسال الاختباري",
+ "admin.marketing.testEmail": "بريد اختباري (اختياري)",
+ "admin.marketing.testEmailPlaceholder": "وإلا: عنوان المرسل SMTP",
+ "admin.marketing.testFailedTitle": "فشل الإرسال الاختباري",
+ "admin.marketing.testRequiredHint": "يتطلب الإرسال الفعلي إرسالًا اختباريًا مسبقًا لنفس هذا المحتوى (يتحقق منه الخادم).",
+ "admin.marketing.testValidated": "تم التحقق من الإرسال الاختباري لهذا المحتوى.",
+ "admin.marketing.title": "التسويق — حملات البريد الإلكتروني",
+ "admin.marketing.unsubscribedNote": "سيتم استثناء {count} من الملغين لاشتراكهم من كل إرسال.",
+ "admin.models.addModel": "إضافة نموذج",
+ "admin.models.businessBadge": "خطة Business",
+ "admin.models.cancel": "إلغاء",
+ "admin.models.catalog": "كتالوج OpenRouter",
+ "admin.models.catalogErrorDesc": "تعذر تحميل كتالوج OpenRouter.",
+ "admin.models.catalogErrorTitle": "الكتالوج غير متاح",
+ "admin.models.customBadge": "مستوى مخصص",
+ "admin.models.customNote": "نماذج حسب الطلب، تُحدد حالة بحالة مع العميل.",
+ "admin.models.defaultBadge": "افتراضي",
+ "admin.models.emptyTier": "لا نماذج: ستُستخدم المجموعة الرسمية للخطة.",
+ "admin.models.enterpriseBadge": "خطة Enterprise",
+ "admin.models.essentialBadge": "مستوى الذكاء الاصطناعي الأساسي",
+ "admin.models.essentialCost": "— يُحتسب بمعامل تكلفة 1",
+ "admin.models.fallbackFirst": "(احتياط رقم 1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — تحقق من رمز المسؤول.",
+ "admin.models.loadErrorTitle": "خطأ في التحميل",
+ "admin.models.matrixDesc": "النماذج النشطة لكل مستوى — ترتيب القائمة هو أولوية الاحتياط. يختار زر الراديو النموذج الافتراضي، ويُطبق فورًا دون إعادة نشر.",
+ "admin.models.matrixTitle": "مصفوفة النماذج حسب الخطة",
+ "admin.models.missingConfig": "لم يتم العثور على إعدادات مستويات الذكاء الاصطناعي. أعد تحميل الصفحة.",
+ "admin.models.moveDown": "تحريك لأسفل",
+ "admin.models.moveUp": "تحريك لأعلى",
+ "admin.models.networkDesc": "لا يمكن الوصول إلى الخادم الخلفي.",
+ "admin.models.networkTitle": "خطأ في الشبكة",
+ "admin.models.premiumBadge": "مستوى الذكاء الاصطناعي المتميز",
+ "admin.models.premiumCost": "— يُحتسب بمعامل تكلفة 5",
+ "admin.models.premiumReservedNote": "محصور في خطتي Business وEnterprise (محرك «openrouter_premium»).",
+ "admin.models.proBadge": "خطة Pro",
+ "admin.models.remove": "إزالة",
+ "admin.models.save": "حفظ",
+ "admin.models.saveErrorTitle": "خطأ في الحفظ",
+ "admin.models.saveNetworkDesc": "تعذر حفظ الإعدادات.",
+ "admin.models.savedDesc": "يُستخدم النموذج الافتراضي الجديد بدءًا من الترجمة التالية، دون إعادة نشر.",
+ "admin.models.savedTitle": "تم حفظ النماذج",
+ "admin.models.saving": "جارٍ الحفظ...",
+ "admin.models.setDefault": "تعيين {model} كنموذج افتراضي",
+ "admin.models.sharedTierNote": "مستوى مشترك: تستخدم خطة Business المستوى الأساسي أيضًا لمحركها «openrouter» — القائمة أعلاه تنطبق على الخطتين.",
+ "admin.models.subtitle": "مصفوفة الخطط → مستويات الذكاء الاصطناعي. النموذج المستخدم فعليًا في كل ترجمة هو نموذج مستوى الخطة: خطة Pro لا يمكنها أبدًا تشغيل نموذج Premium.",
+ "admin.models.title": "النماذج والاشتراكات",
+ "admin.stats.aiTiers": "مستويات الذكاء الاصطناعي (30 يومًا)",
+ "admin.stats.includingCredits": "منها {amount} من الرصيد الإضافي",
+ "admin.stats.mrr": "الإيراد الشهري المتكرر المقدر",
+ "admin.stats.noTranslationsYet": "لا توجد ترجمات مسجلة خلال آخر 30 يومًا: ستكتمل توزيعات المستويات مع الترجمات القادمة.",
+ "admin.stats.payments30": "{count} دفعة خلال 30 يومًا",
+ "admin.stats.refreshing": "جارٍ التحديث...",
+ "admin.stats.revenue30": "الإيرادات (30 يومًا)",
+ "admin.stats.revenueTotal": "إجمالي الإيرادات المحصلة",
+ "admin.stats.tiersSub": "أساسي / متميز · كلاسيكي {classic} · آخر {other}",
+ "admin.stats.unavailable": "إحصائيات الأعمال غير متاحة ({error}).",
+ "admin.stats.waitlist": "قائمة الانتظار",
+ "admin.stats.waitlistSub": "مُدرجون في قائمة الانتظار"
}
diff --git a/frontend/src/lib/i18n/messages/ar/landing.json b/frontend/src/lib/i18n/messages/ar/landing.json
index bd95edb..1e8ade5 100644
--- a/frontend/src/lib/i18n/messages/ar/landing.json
+++ b/frontend/src/lib/i18n/messages/ar/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "للمحترفين المتميزين",
"landing.pricing.pro.f1": "200 مستند / شهر",
"landing.pricing.pro.f2": "حتى 200 صفحة لكل مستند",
- "landing.pricing.pro.f3": "ترجمة بالذكاء الاصطناعي",
+ "landing.pricing.pro.f3": "ذكاء اصطناعي أساسي: DeepSeek V4 Flash، GLM-5.3 Flash، MiniMax M3",
"landing.pricing.pro.f4": "Google مشمولان",
"landing.pricing.pro.f5": "قواميس وأوامر مخصصة",
"landing.pricing.pro.f6": "دعم ذو أولوية",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "للفرق ذات الاحتياجات الكبيرة",
"landing.pricing.business.f1": "1,000 مستند / شهر",
"landing.pricing.business.f2": "حتى 500 صفحة لكل مستند",
- "landing.pricing.business.f3": "ذكاء اصطناعي متميز (Claude)",
+ "landing.pricing.business.f3": "ذكاء اصطناعي متميز (Claude 5)",
"landing.pricing.business.f4": "جميع المزودين + واجهة برمجة التطبيقات",
"landing.pricing.business.f5": "خطاطف الويب والأتمتة",
"landing.pricing.business.f6": "5 مقاعد للفريق",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "ملفات DOCX, XLSX, PPTX أو PDF مدعومة",
"landing.translate.aiAnalysis": "تحليل AI نشط",
"landing.translate.processing": "جاري المعالجة",
- "landing.translate.preservingLayout": "جاري الحفاظ على التنسيق"
+ "landing.translate.preservingLayout": "جاري الحفاظ على التنسيق",
+ "landing.beforeAfter.seal": "نفس التخطيط، كلمة بكلمة",
+ "landing.beforeAfter.proof1": "مخططات SmartArt يُعاد بناؤها، وليس تسطيحها",
+ "landing.beforeAfter.proof2": "سلاسل الرسوم البيانية ومحاورها مترجمة",
+ "landing.beforeAfter.proof3": "جداول المحتويات يُعاد إنشاؤها باللغة الهدف",
+ "landing.beforeAfter.targetTitle": "المواصفات الفنية — معالجة الهواء",
+ "landing.beforeAfter.targetPara1": "يجب تشغيل نظام التهوية قبل نهاية الربع الثاني.",
+ "landing.beforeAfter.targetPara2Before": "",
+ "landing.beforeAfter.targetTerm": "وحدة معالجة الهواء",
+ "landing.beforeAfter.targetPara2After": "يجب أن تتوافق مع فئة الترشيح F7 وفقًا للجدول.",
+ "landing.beforeAfter.targetItem1": "الحمل الحراري: 42 كيلوواط عند التدفق الاسمي",
+ "landing.beforeAfter.targetItem2": "مستوى الصوت أقل من 45 dB(A) على مسافة 3 أمتار",
+ "landing.formats.pill": "التوافق",
+ "landing.hero.visualCaption": "نفس التخطيط، لغة جديدة — لا شيء آخر يتغير",
+ "landing.pricing.free.name": "مجاني",
+ "landing.pricing.free.desc": "مثالي لاكتشاف التطبيق",
+ "landing.pricing.free.cta": "اختر هذه الخطة",
+ "landing.pricing.enterprise.name": "المؤسسات",
+ "landing.pricing.enterprise.desc": "حلول مخصصة للمؤسسات الكبيرة",
+ "landing.pricing.enterprise.cta": "اتصل بنا",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/ar/pricing.json b/frontend/src/lib/i18n/messages/ar/pricing.json
index 00285f5..fbb0739 100644
--- a/frontend/src/lib/i18n/messages/ar/pricing.json
+++ b/frontend/src/lib/i18n/messages/ar/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "سجل 90 يومًا",
"pricing.plans.business.feat1": "1 000 مستند / شهر",
"pricing.plans.business.feat2": "حتى 500 صفحة لكل مستند",
- "pricing.plans.business.feat3": "AI أساسية + متميزة (Claude Haiku)",
+ "pricing.plans.business.feat3": "الذكاء الاصطناعي الأساسي + المتميز (Claude 5)",
"pricing.plans.business.feat4": "جميع مزوّدي الترجمة",
"pricing.plans.business.feat5": "ملفات حتى 50 ميغابايت",
"pricing.plans.business.feat6": "وصول API (10 000 استدعاء/شهر)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "سجل سنة واحدة",
"pricing.plans.business.feat10": "تحليلات متقدمة",
"pricing.plans.enterprise.feat1": "مستندات بلا حدود",
- "pricing.plans.enterprise.feat2": "جميع نماذج AI (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "جميع نماذج AI (Claude 5، DeepSeek V4 Pro، GLM-5.3…)",
"pricing.plans.enterprise.feat3": "نشر محلي أو سحابة مخصصة",
"pricing.plans.enterprise.feat4": "SLA 99.9% مضمون",
"pricing.plans.enterprise.feat5": "دعم مخصص على مدار الساعة",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "ذكاء اصطناعي متعدد المسارات فائق السرعة",
"pricing.trust.availability.title": "متاح على مدار الساعة",
"pricing.trust.availability.sub": "ضمان وقت تشغيل 99.9%",
- "pricing.aiModels.title": "نماذج الذكاء الاصطناعي — مارس 2026",
+ "pricing.aiModels.title": "نماذج الذكاء الاصطناعي — سبتمبر 2026",
"pricing.aiModels.essential.title": "ترجمة AI أساسية",
"pricing.aiModels.essential.plan": "خطة Pro",
"pricing.aiModels.essential.descPrefix": "مبني على",
- "pricing.aiModels.essential.descSuffix": "— أنموذج الذكاء الاصطناعي الأكثر فعالية من حيث التكلفة لعام 2026. جودة مماثلة للنماذج المتقدمة بتكلفة أقل بكثير.",
- "pricing.aiModels.essential.modelName": "نموذج الذكاء الاصطناعي الأساسي",
- "pricing.aiModels.essential.context": "163K رمز سياقي",
- "pricing.aiModels.essential.value": "قيمة ممتازة مقابل المال",
+ "pricing.aiModels.essential.descSuffix": "— نماذج الذكاء الاصطناعي الأفضل قيمة مقابل المال لعام 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash وGLM-5.3 Flash وMiniMax M3",
+ "pricing.aiModels.essential.context": "سياق يصل إلى 1.3 مليون رمز (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "أفضل قيمة مقابل السعر",
"pricing.aiModels.premium.title": "ترجمة AI متميزة",
"pricing.aiModels.premium.plan": "خطة Business",
"pricing.aiModels.premium.descPrefix": "مبني على",
"pricing.aiModels.premium.descSuffix": "من Anthropic — دقة عالية في المستندات القانونية والطبية والتقنية المعقدة.",
- "pricing.aiModels.premium.context": "200K رمز سياقي",
+ "pricing.aiModels.premium.context": "مليون رمز سياقي",
"pricing.aiModels.premium.precision": "أعلى دقة",
"pricing.faq.title": "الأسئلة الشائعة",
"pricing.faq.q1": "هل يمكنني تغيير خطتي في أي وقت؟",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "ما هي «ترجمة AI الأساسية»؟",
"pricing.faq.a2": "إنه محرك الذكاء الاصطناعي الخاص بنا. يفهم سياق مستنداتك، ويحافظ على التخطيط، ويتعامل مع المصطلحات التقنية بشكل أفضل بكثير من الترجمة الكلاسيكية.",
"pricing.faq.q3": "ما الفرق بين AI الأساسية وAI المتميزة؟",
- "pricing.faq.a3": "الذكاء الاصطناعي الأساسي يستخدم نموذجًا محسّنًا (قيمة ممتازة مقابل المال). الذكاء الاصطناعي المتميز يستخدم Claude 3.5 Haiku من Anthropic، أكثر دقة في المستندات القانونية والطبية والتقنية المعقدة.",
+ "pricing.faq.a3": "الذكاء الاصطناعي الأساسي يعمل بنماذج DeepSeek V4 Flash وGLM-5.3 Flash وMiniMax M3 (قيمة ممتازة مقابل المال). الذكاء الاصطناعي المتميز يستخدم Claude 5 من Anthropic، أكثر دقة في المستندات القانونية والطبية والتقنية المعقدة.",
"pricing.faq.q4": "هل تُحفظ مستنداتي بعد الترجمة؟",
"pricing.faq.a4": "الملفات المترجمة متاحة حسب خطتك (30 يومًا لـ Starter، 90 يومًا لـ Pro، سنة واحدة لـ Business). وهي مشفرة أثناء التخزين والنقل.",
"pricing.faq.q5": "ماذا يحدث إذا تجاوزت حصتي الشهرية؟",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "خطأ أثناء إنشاء الدفع.",
"pricing.dashboard": "لوحة التحكم",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "ابتداءً من 0.09 دولار لكل مليون رمز",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "بدائل: DeepSeek V4 Pro وGLM-5.3",
+ "pricing.confirm.title": "أكّد اشتراكك",
+ "pricing.confirm.subtitle": "سيتم تحويلك إلى مزود الدفع الآمن لدينا.",
+ "pricing.confirm.cancel": "إلغاء",
+ "pricing.confirm.cta": "المتابعة إلى الدفع",
+ "pricing.confirm.year": "سنة",
+ "pricing.confirm.month": "شهر",
+ "pricing.confirm.monthlyEquivalent": "فوترة سنوية — أي {price} € / شهر.",
+ "pricing.confirm.secureNote": "يمكنك الإلغاء في أي وقت من ملفك الشخصي. تتم معالجة الدفع عبر Stripe؛ ولا تمر بيانات بطاقتك أبدًا عبر خوادمنا.",
+ "pricing.enterprise.subject": "عرض المؤسسات",
+ "pricing.enterpriseBand.cta": "اتصل بنا",
+ "pricing.enterpriseBand.text": "حجم كبير، محركات مخصصة، خيارات تثبيت داخلي — لنتحدث.",
+ "pricing.error.server": "خطأ في الخادم {status}",
+ "pricing.freeBand.cta": "ابدأ مجانًا",
+ "pricing.freeBand.text": "تريد التجربة فقط؟ ابدأ مجانًا — 5 مستندات شهريًا، بدون بطاقة.",
+ "pricing.header.titleBase": "خطة لكل",
+ "pricing.header.titleAccent": "احتياج",
+ "pricing.toast.close": "إغلاق",
+ "pricing.aiModels.premium.price": "2 $ / 10 $ لكل مليون رمز"
}
diff --git a/frontend/src/lib/i18n/messages/ar/providerTheme.json b/frontend/src/lib/i18n/messages/ar/providerTheme.json
index 464f7c7..0b5c97f 100644
--- a/frontend/src/lib/i18n/messages/ar/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/ar/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "وصول موحد إلى أفضل النماذج مفتوحة المصدر المحسّنة للترجمة.",
"providerTheme.openrouter_premium.badge": "فائق",
"providerTheme.openrouter_premium.subBadge": "السياق الأقصى",
- "providerTheme.openrouter_premium.desc": "مدعوم من أحدث النماذج (GPT-4o، Claude Sonnet 4.6) للمستندات الطويلة.",
+ "providerTheme.openrouter_premium.desc": "مدعوم من أحدث النماذج (GPT-4o، Claude 5) للمستندات الطويلة.",
"providerTheme.zai.badge": "متخصص",
"providerTheme.zai.subBadge": "المال والقانون",
"providerTheme.zai.desc": "نموذج مضبوط بدقة للمصطلحات التجارية المتطلبة (قانونية، مالية).",
diff --git a/frontend/src/lib/i18n/messages/de/admin.json b/frontend/src/lib/i18n/messages/de/admin.json
index 9efbdd7..c499253 100644
--- a/frontend/src/lib/i18n/messages/de/admin.json
+++ b/frontend/src/lib/i18n/messages/de/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Anbieter",
"admin.nav.system": "System",
"admin.nav.logs": "Protokolle",
+ "admin.nav.stats": "Statistiken",
"admin.users.title": "Benutzerverwaltung",
"admin.users.subtitle": "Benutzerkonten anzeigen und verwalten",
"admin.users.planUpdated": "Plan aktualisiert",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "Warte auf Daten...",
"admin.system.purging": "Bereinigung läuft...",
"admin.system.clean": "Bereinigen",
- "admin.system.purge": "Bereinigen"
+ "admin.system.purge": "Bereinigen",
+ "admin.nav.models": "Modelle & Abonnements",
+ "admin.nav.marketing": "Marketing",
+ "admin.marketing.audience": "Zielgruppe",
+ "admin.marketing.audienceAllUsers": "Alle Konten",
+ "admin.marketing.audienceInactive": "Seit 30 Tagen inaktive Konten",
+ "admin.marketing.audiencePlaceholder": "Zielgruppe wählen",
+ "admin.marketing.audiencePlanBusiness": "Business-Tarif",
+ "admin.marketing.audiencePlanEnterprise": "Enterprise-Tarif",
+ "admin.marketing.audiencePlanFree": "Kostenlos-Tarif",
+ "admin.marketing.audiencePlanPro": "Pro-Tarif",
+ "admin.marketing.audiencePlanStarter": "Starter-Tarif",
+ "admin.marketing.audienceWaitlist": "Warteliste",
+ "admin.marketing.badgeReal": "echt",
+ "admin.marketing.badgeTest": "Test",
+ "admin.marketing.confirmSend": "Diese E-Mail an {count} Empfänger senden („{audience}“)?",
+ "admin.marketing.footerNote": "Eine Fußzeile mit dem Abmeldelink wird beim Senden automatisch ergänzt.",
+ "admin.marketing.hidePreview": "Vorschau ausblenden",
+ "admin.marketing.history": "Sendungsverlauf",
+ "admin.marketing.historyFailedSuffix": ", {count} Fehler",
+ "admin.marketing.historySent": "{sent}/{total} gesendet",
+ "admin.marketing.html": "HTML-Inhalt",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Bitte Betreff und HTML ausfüllen.",
+ "admin.marketing.incompleteTitle": "Unvollständiger Inhalt",
+ "admin.marketing.loadErrorTitle": "Ladefehler",
+ "admin.marketing.localCheckUnavailable": "Lokale Prüfung in diesem Browser nicht möglich: Der Server kontrolliert die vorherige Testsendung.",
+ "admin.marketing.networkDesc": "Backend nicht erreichbar.",
+ "admin.marketing.newCampaign": "Neue Kampagne",
+ "admin.marketing.noHistory": "Keine Sendung erfasst.",
+ "admin.marketing.preview": "Vorschau",
+ "admin.marketing.previewEmpty": "Vorschau: HTML daneben einfügen.",
+ "admin.marketing.previewTitle": "E-Mail-Vorschau",
+ "admin.marketing.recipientsAvailable": "{count} Empfänger nach Ausschluss der Abmeldungen verfügbar.",
+ "admin.marketing.sendReal": "An {count} Empfänger senden",
+ "admin.marketing.sendRefusedTitle": "Sendung abgelehnt",
+ "admin.marketing.sendTest": "Testsendung",
+ "admin.marketing.sendingDesc": "{count} E-Mail(s) in der Warteschlange (0,2 s Abstand). Abmeldungen werden ausgeschlossen.",
+ "admin.marketing.sendingTitle": "Sendung läuft",
+ "admin.marketing.subject": "Betreff",
+ "admin.marketing.subjectPlaceholder": "z. B.: Ihre Übersetzung wartet — 20 % für 7 Tage",
+ "admin.marketing.subtitle": "E-Mails an eine Zielgruppe senden, mit Pflicht-Testsendung, automatischem Abmeldelink und vollständigem Verlauf.",
+ "admin.marketing.testDoneDesc": "E-Mail erhalten an {email}",
+ "admin.marketing.testDoneTitle": "Testsendung erfolgt",
+ "admin.marketing.testEmail": "Test-E-Mail (optional)",
+ "admin.marketing.testEmailPlaceholder": "Sonst: SMTP-Absenderadresse",
+ "admin.marketing.testFailedTitle": "Testsendung fehlgeschlagen",
+ "admin.marketing.testRequiredHint": "Eine echte Sendung erfordert eine vorherige Testsendung exakt dieses Inhalts (vom Server geprüft).",
+ "admin.marketing.testValidated": "Testsendung für diesen Inhalt bestätigt.",
+ "admin.marketing.title": "Marketing — E-Mail-Kampagnen",
+ "admin.marketing.unsubscribedNote": "{count} abgemeldete Kontakte werden von jeder Sendung ausgeschlossen.",
+ "admin.models.addModel": "Modell hinzufügen",
+ "admin.models.businessBadge": "Business-Tarif",
+ "admin.models.cancel": "Abbrechen",
+ "admin.models.catalog": "OpenRouter-Katalog",
+ "admin.models.catalogErrorDesc": "OpenRouter-Katalog konnte nicht geladen werden.",
+ "admin.models.catalogErrorTitle": "Katalog nicht verfügbar",
+ "admin.models.customBadge": "Individuelle Stufe",
+ "admin.models.customNote": "Sondermodelle, im Einzelfall mit dem Kunden vereinbart.",
+ "admin.models.defaultBadge": "Standard",
+ "admin.models.emptyTier": "Kein Modell: Die offizielle Modellrange des Tarifs wird verwendet.",
+ "admin.models.enterpriseBadge": "Enterprise-Tarif",
+ "admin.models.essentialBadge": "KI-Stufe Basis",
+ "admin.models.essentialCost": "— Abrechnungsfaktor 1",
+ "admin.models.fallbackFirst": "(Fallback Nr. 1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — prüfen Sie Ihr Admin-Token.",
+ "admin.models.loadErrorTitle": "Ladefehler",
+ "admin.models.matrixDesc": "Aktive Modelle pro Stufe — die Reihenfolge der Liste ist die Fallback-Priorität. Der Radioknopf wählt das Standardmodell, sofort und ohne neues Deployment wirksam.",
+ "admin.models.matrixTitle": "Modellmatrix pro Tarif",
+ "admin.models.missingConfig": "KI-Stufen-Konfiguration nicht gefunden. Bitte Seite neu laden.",
+ "admin.models.moveDown": "Nach unten",
+ "admin.models.moveUp": "Nach oben",
+ "admin.models.networkDesc": "Backend nicht erreichbar.",
+ "admin.models.networkTitle": "Netzwerkfehler",
+ "admin.models.premiumBadge": "KI-Stufe Premium",
+ "admin.models.premiumCost": "— Abrechnungsfaktor 5",
+ "admin.models.premiumReservedNote": "Den Tarifen Business und Enterprise vorbehalten (Engine „openrouter_premium“).",
+ "admin.models.proBadge": "Pro-Tarif",
+ "admin.models.remove": "Entfernen",
+ "admin.models.save": "Speichern",
+ "admin.models.saveErrorTitle": "Speicherfehler",
+ "admin.models.saveNetworkDesc": "Konfiguration konnte nicht gespeichert werden.",
+ "admin.models.savedDesc": "Das neue Standardmodell wird ab der nächsten Übersetzung verwendet — ohne neues Deployment.",
+ "admin.models.savedTitle": "Modelle gespeichert",
+ "admin.models.saving": "Speichern...",
+ "admin.models.setDefault": "{model} als Standardmodell festlegen",
+ "admin.models.sharedTierNote": "Gemeinsame Stufe: Der Business-Tarif nutzt die Basis-Stufe ebenfalls für seine Engine „openrouter“ — die obige Liste gilt für beide Tarife.",
+ "admin.models.subtitle": "Matrix Tarife → KI-Stufen. Das tatsächlich pro Übersetzung verwendete Modell ist das der KI-Stufe des Tarifs: Ein Pro-Tarif kann nie ein Premium-Modell auslösen.",
+ "admin.models.title": "Modelle & Abonnements",
+ "admin.stats.aiTiers": "KI-Stufen (30 T)",
+ "admin.stats.includingCredits": "davon {amount} Credits",
+ "admin.stats.mrr": "Geschätzter MRR",
+ "admin.stats.noTranslationsYet": "Keine Übersetzungen der letzten 30 Tage in der Datenbank: Die Stufenverteilung füllt sich mit den nächsten Übersetzungen.",
+ "admin.stats.payments30": "{count} Zahlung(en) in 30 Tagen",
+ "admin.stats.refreshing": "Aktualisierung...",
+ "admin.stats.revenue30": "Umsatz (30 Tage)",
+ "admin.stats.revenueTotal": "Eingenommener Umsatz (gesamt)",
+ "admin.stats.tiersSub": "Basis / Premium · klassisch {classic} · sonstige {other}",
+ "admin.stats.unavailable": "Geschäftszahlen nicht verfügbar ({error}).",
+ "admin.stats.waitlist": "Warteliste",
+ "admin.stats.waitlistSub": "Einträge auf der Warteliste"
}
diff --git a/frontend/src/lib/i18n/messages/de/landing.json b/frontend/src/lib/i18n/messages/de/landing.json
index 8b26b6e..6ebf0d4 100644
--- a/frontend/src/lib/i18n/messages/de/landing.json
+++ b/frontend/src/lib/i18n/messages/de/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "Für anspruchsvolle Profis",
"landing.pricing.pro.f1": "200 Dokumente / Monat",
"landing.pricing.pro.f2": "Bis zu 200 Seiten pro Dokument",
- "landing.pricing.pro.f3": "KI-gestützte Übersetzung",
+ "landing.pricing.pro.f3": "Basis-KI: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google inklusive",
"landing.pricing.pro.f5": "Individuelle Glossare & Prompts",
"landing.pricing.pro.f6": "Prioritäts-Support",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "Für Teams mit hohem Bedarf",
"landing.pricing.business.f1": "1 000 Dokumente / Monat",
"landing.pricing.business.f2": "Bis zu 500 Seiten pro Dokument",
- "landing.pricing.business.f3": "Premium-KI (Claude)",
+ "landing.pricing.business.f3": "Premium-KI (Claude 5)",
"landing.pricing.business.f4": "Alle Anbieter + API-Zugang",
"landing.pricing.business.f5": "Webhooks & Automatisierung",
"landing.pricing.business.f6": "5 Teamplätze",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "DOCX, XLSX, PPTX oder PDF Dateien unterstützt",
"landing.translate.aiAnalysis": "KI-Analyse aktiv",
"landing.translate.processing": "Verarbeitung läuft",
- "landing.translate.preservingLayout": "Ihr Layout wird beibehalten"
+ "landing.translate.preservingLayout": "Ihr Layout wird beibehalten",
+ "landing.beforeAfter.seal": "Gleiches Layout, Wort für Wort",
+ "landing.beforeAfter.proof1": "SmartArt-Diagramme werden neu aufgebaut, nicht abgeflacht",
+ "landing.beforeAfter.proof2": "Diagrammreihen und Achsen übersetzt",
+ "landing.beforeAfter.proof3": "Verzeichnisse in der Zielsprache neu erstellt",
+ "landing.beforeAfter.targetTitle": "Technisches Datenblatt — Luftaufbereitung",
+ "landing.beforeAfter.targetPara1": "Die Lüftungsanlage muss vor Ende des zweiten Quartals in Betrieb genommen werden.",
+ "landing.beforeAfter.targetPara2Before": "Die",
+ "landing.beforeAfter.targetTerm": "Lüftungseinheit",
+ "landing.beforeAfter.targetPara2After": "muss gemäß Planung der Filterklasse F7 entsprechen.",
+ "landing.beforeAfter.targetItem1": "Wärmebelastung: 42 kW bei Nenndurchfluss",
+ "landing.beforeAfter.targetItem2": "Schallpegel unter 45 dB(A) bei 3 m",
+ "landing.formats.pill": "KOMPATIBILITÄT",
+ "landing.hero.visualCaption": "Gleiches Layout, neue Sprache — nichts anderes verändert sich",
+ "landing.pricing.free.name": "Kostenlos",
+ "landing.pricing.free.desc": "Ideal zum Entdecken der App",
+ "landing.pricing.free.cta": "Diesen Plan wählen",
+ "landing.pricing.enterprise.name": "Enterprise",
+ "landing.pricing.enterprise.desc": "Maßgeschneiderte Lösungen für große Organisationen",
+ "landing.pricing.enterprise.cta": "Kontakt aufnehmen",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/de/pricing.json b/frontend/src/lib/i18n/messages/de/pricing.json
index 97329e0..8ff2e3e 100644
--- a/frontend/src/lib/i18n/messages/de/pricing.json
+++ b/frontend/src/lib/i18n/messages/de/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "90 Tage Verlauf",
"pricing.plans.business.feat1": "1.000 Dokumente / Monat",
"pricing.plans.business.feat2": "Bis zu 500 Seiten pro Dokument",
- "pricing.plans.business.feat3": "Basis- + Premium-KI (Claude Haiku)",
+ "pricing.plans.business.feat3": "Basis-KI + Premium-KI (Claude 5)",
"pricing.plans.business.feat4": "Alle Übersetzungsanbieter",
"pricing.plans.business.feat5": "Dateien bis zu 50 MB",
"pricing.plans.business.feat6": "API-Zugang (10.000 Aufrufe/Monat)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "1 Jahr Verlauf",
"pricing.plans.business.feat10": "Erweiterte Analysen",
"pricing.plans.enterprise.feat1": "Unbegrenzte Dokumente",
- "pricing.plans.enterprise.feat2": "Alle KI-Modelle (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "Alle KI-Modelle (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "On-Premise oder dedizierte Cloud",
"pricing.plans.enterprise.feat4": "99,9 % SLA garantiert",
"pricing.plans.enterprise.feat5": "24/7 dedizierter Support",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "Ultraschnelle Multi-Thread-KI",
"pricing.trust.availability.title": "24/7 verfügbar",
"pricing.trust.availability.sub": "99,9 % garantierte Verfügbarkeit",
- "pricing.aiModels.title": "Unsere KI-Modelle — März 2026",
+ "pricing.aiModels.title": "Unsere KI-Modelle — September 2026",
"pricing.aiModels.essential.title": "KI-Basisübersetzung",
"pricing.aiModels.essential.plan": "Pro-Plan",
"pricing.aiModels.essential.descPrefix": "Basierend auf",
- "pricing.aiModels.essential.descSuffix": "— dem kosteneffizientesten KI-Modell 2026. Qualität vergleichbar mit Frontier-Modellen zu einem Bruchteil der Kosten.",
- "pricing.aiModels.essential.modelName": "unser Essentielles KI-Modell",
- "pricing.aiModels.essential.context": "163K Token Kontext",
- "pricing.aiModels.essential.value": "Hervorragendes Preis-Leistungs-Verhältnis",
+ "pricing.aiModels.essential.descSuffix": "— die KI-Modelle mit dem besten Preis-Leistungs-Verhältnis 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash und MiniMax M3",
+ "pricing.aiModels.essential.context": "Bis zu 1,3 M Kontext (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "Das beste Preis-Leistungs-Verhältnis",
"pricing.aiModels.premium.title": "KI-Premiumübersetzung",
"pricing.aiModels.premium.plan": "Business-Plan",
"pricing.aiModels.premium.descPrefix": "Basierend auf",
"pricing.aiModels.premium.descSuffix": "von Anthropic — präzise bei juristischen, medizinischen und komplexen technischen Dokumenten.",
- "pricing.aiModels.premium.context": "200K Token Kontext",
+ "pricing.aiModels.premium.context": "1M Kontext",
"pricing.aiModels.premium.precision": "Höchste Genauigkeit",
"pricing.faq.title": "Häufig gestellte Fragen",
"pricing.faq.q1": "Kann ich jederzeit den Plan wechseln?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "Was ist die «KI-Basisübersetzung»?",
"pricing.faq.a2": "Unser KI-Motor versteht den Kontext Ihrer Dokumente, erhält das Layout und verarbeitet Fachbegriffe deutlich besser als klassische Übersetzungen.",
"pricing.faq.q3": "Was ist der Unterschied zwischen Basis- und Premium-KI?",
- "pricing.faq.a3": "Die Basis-KI nutzt ein optimiertes Modell (hervorragendes Preis-Leistungs-Verhältnis). Die Premium-KI verwendet Claude 3.5 Haiku von Anthropic und ist genauer bei juristischen, medizinischen und komplexen technischen Dokumenten.",
+ "pricing.faq.a3": "Die Basis-KI nutzt DeepSeek V4 Flash, GLM-5.3 Flash und MiniMax M3 (hervorragendes Preis-Leistungs-Verhältnis). Die Premium-KI verwendet Claude 5 von Anthropic und ist genauer bei juristischen, medizinischen und komplexen technischen Dokumenten.",
"pricing.faq.q4": "Werden meine Dokumente nach der Übersetzung gespeichert?",
"pricing.faq.a4": "Übersetzte Dateien sind je nach Plan verfügbar (30 Tage Starter, 90 Tage Pro, 1 Jahr Business). Sie sind im Ruhezustand und bei der Übertragung verschlüsselt.",
"pricing.faq.q5": "Was passiert, wenn ich mein monatliches Kontingent überschreite?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "Fehler beim Erstellen der Zahlung.",
"pricing.dashboard": "Dashboard",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "Ab 0,09 $ pro Million Token",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Alternativen: DeepSeek V4 Pro und GLM-5.3",
+ "pricing.confirm.title": "Bestätigen Sie Ihr Abonnement",
+ "pricing.confirm.subtitle": "Sie werden zu unserem sicheren Zahlungsanbieter weitergeleitet.",
+ "pricing.confirm.cancel": "Abbrechen",
+ "pricing.confirm.cta": "Weiter zur Zahlung",
+ "pricing.confirm.year": "Jahr",
+ "pricing.confirm.month": "Monat",
+ "pricing.confirm.monthlyEquivalent": "Jährliche Abrechnung — entspricht {price} € / Monat.",
+ "pricing.confirm.secureNote": "Jederzeit über Ihr Profil kündbar. Die Zahlung wird von Stripe abgewickelt; Ihre Kartendaten erreichen nie unsere Server.",
+ "pricing.enterprise.subject": "Enterprise-Angebot",
+ "pricing.enterpriseBand.cta": "Kontakt aufnehmen",
+ "pricing.enterpriseBand.text": "Volumen, dedizierte Engines, On-Premise-Optionen — sprechen wir darüber.",
+ "pricing.error.server": "Serverfehler {status}",
+ "pricing.freeBand.cta": "Kostenlos starten",
+ "pricing.freeBand.text": "Nur ausprobieren? Kostenlos starten — 5 Dokumente pro Monat, ohne Karte.",
+ "pricing.header.titleBase": "Ein Plan für",
+ "pricing.header.titleAccent": "jeden Bedarf",
+ "pricing.toast.close": "Schließen",
+ "pricing.aiModels.premium.price": "2 $ / 10 $ pro 1M Token"
}
diff --git a/frontend/src/lib/i18n/messages/de/providerTheme.json b/frontend/src/lib/i18n/messages/de/providerTheme.json
index 7e3a54a..4c460f1 100644
--- a/frontend/src/lib/i18n/messages/de/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/de/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Einheitlicher Zugriff auf die besten Open-Source-Modelle, optimiert für Übersetzung.",
"providerTheme.openrouter_premium.badge": "Ultra",
"providerTheme.openrouter_premium.subBadge": "Maximaler Kontext",
- "providerTheme.openrouter_premium.desc": "Unterstützt durch modernste Modelle (GPT-4o, Claude Sonnet 4.6) für lange Dokumente.",
+ "providerTheme.openrouter_premium.desc": "Unterstützt durch modernste Modelle (GPT-4o, Claude 5) für lange Dokumente.",
"providerTheme.zai.badge": "Spezialisiert",
"providerTheme.zai.subBadge": "Finanzen & Recht",
"providerTheme.zai.desc": "Modell feinabgestimmt auf anspruchsvolle Geschäftsterminologien (Recht, Finanzen).",
diff --git a/frontend/src/lib/i18n/messages/en/admin.json b/frontend/src/lib/i18n/messages/en/admin.json
index a95dbd6..abacd23 100644
--- a/frontend/src/lib/i18n/messages/en/admin.json
+++ b/frontend/src/lib/i18n/messages/en/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Providers",
"admin.nav.system": "System",
"admin.nav.logs": "Logs",
+ "admin.nav.stats": "Statistics",
"admin.users.title": "User Management",
"admin.users.subtitle": "View and manage user accounts",
"admin.users.planUpdated": "Plan updated",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "Waiting for data...",
"admin.system.purging": "Purging...",
"admin.system.clean": "Clean",
- "admin.system.purge": "Purge"
+ "admin.system.purge": "Purge",
+ "admin.nav.models": "Models & subscriptions",
+ "admin.nav.marketing": "Marketing",
+ "admin.marketing.audience": "Audience",
+ "admin.marketing.audienceAllUsers": "All accounts",
+ "admin.marketing.audienceInactive": "Accounts inactive for 30 days",
+ "admin.marketing.audiencePlaceholder": "Choose an audience",
+ "admin.marketing.audiencePlanBusiness": "Business plan",
+ "admin.marketing.audiencePlanEnterprise": "Enterprise plan",
+ "admin.marketing.audiencePlanFree": "Free plan",
+ "admin.marketing.audiencePlanPro": "Pro plan",
+ "admin.marketing.audiencePlanStarter": "Starter plan",
+ "admin.marketing.audienceWaitlist": "Waitlist",
+ "admin.marketing.badgeReal": "real",
+ "admin.marketing.badgeTest": "test",
+ "admin.marketing.confirmSend": "Send this email to {count} recipient(s) (“{audience}”)?",
+ "admin.marketing.footerNote": "A footer with the unsubscribe link is automatically added when sending.",
+ "admin.marketing.hidePreview": "Hide preview",
+ "admin.marketing.history": "Send history",
+ "admin.marketing.historyFailedSuffix": ", {count} failed",
+ "admin.marketing.historySent": "{sent}/{total} sent",
+ "admin.marketing.html": "HTML content",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Please fill in the subject and the HTML.",
+ "admin.marketing.incompleteTitle": "Incomplete content",
+ "admin.marketing.loadErrorTitle": "Loading error",
+ "admin.marketing.localCheckUnavailable": "Local check unavailable in this browser: the server will verify the prior test send.",
+ "admin.marketing.networkDesc": "Cannot reach the backend.",
+ "admin.marketing.newCampaign": "New campaign",
+ "admin.marketing.noHistory": "No send recorded.",
+ "admin.marketing.preview": "Preview",
+ "admin.marketing.previewEmpty": "Preview: paste your HTML next to this panel.",
+ "admin.marketing.previewTitle": "Email preview",
+ "admin.marketing.recipientsAvailable": "{count} recipient(s) available after excluding unsubscribed contacts.",
+ "admin.marketing.sendReal": "Send to {count} recipient(s)",
+ "admin.marketing.sendRefusedTitle": "Send refused",
+ "admin.marketing.sendTest": "Send test",
+ "admin.marketing.sendingDesc": "{count} email(s) queued for sending (0.2 s interval). Unsubscribed contacts are excluded.",
+ "admin.marketing.sendingTitle": "Send in progress",
+ "admin.marketing.subject": "Subject",
+ "admin.marketing.subjectPlaceholder": "E.g.: Your translation is waiting — 20% off for 7 days",
+ "admin.marketing.subtitle": "Send emails to an audience, with a mandatory test send, automatic unsubscribe link and full history.",
+ "admin.marketing.testDoneDesc": "Email received at {email}",
+ "admin.marketing.testDoneTitle": "Test send done",
+ "admin.marketing.testEmail": "Test email (optional)",
+ "admin.marketing.testEmailPlaceholder": "Otherwise: SMTP sender address",
+ "admin.marketing.testFailedTitle": "Test send failed",
+ "admin.marketing.testRequiredHint": "A real send requires a prior test send of this exact content (verified by the server).",
+ "admin.marketing.testValidated": "Test send validated for this content.",
+ "admin.marketing.title": "Marketing — email campaigns",
+ "admin.marketing.unsubscribedNote": "{count} unsubscribed contact(s) will be excluded from any send.",
+ "admin.models.addModel": "Add a model",
+ "admin.models.businessBadge": "Business plan",
+ "admin.models.cancel": "Cancel",
+ "admin.models.catalog": "OpenRouter catalog",
+ "admin.models.catalogErrorDesc": "Failed to load the OpenRouter catalog.",
+ "admin.models.catalogErrorTitle": "Catalog unavailable",
+ "admin.models.customBadge": "Custom tier",
+ "admin.models.customNote": "Bespoke models, agreed case by case with the customer.",
+ "admin.models.defaultBadge": "default",
+ "admin.models.emptyTier": "No model: the plan's official range will be used.",
+ "admin.models.enterpriseBadge": "Enterprise plan",
+ "admin.models.essentialBadge": "Essential AI tier",
+ "admin.models.essentialCost": "— billed at cost factor 1",
+ "admin.models.fallbackFirst": "(fallback #1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — check your admin token.",
+ "admin.models.loadErrorTitle": "Loading error",
+ "admin.models.matrixDesc": "Active models per tier — the list order is the fallback priority. The radio button picks the default model, applied immediately without redeployment.",
+ "admin.models.matrixTitle": "Model matrix by plan",
+ "admin.models.missingConfig": "AI tier configuration not found. Reload the page.",
+ "admin.models.moveDown": "Move down",
+ "admin.models.moveUp": "Move up",
+ "admin.models.networkDesc": "Cannot reach the backend.",
+ "admin.models.networkTitle": "Network error",
+ "admin.models.premiumBadge": "Premium AI tier",
+ "admin.models.premiumCost": "— billed at cost factor 5",
+ "admin.models.premiumReservedNote": "Reserved for Business and Enterprise plans (“openrouter_premium” engine).",
+ "admin.models.proBadge": "Pro plan",
+ "admin.models.remove": "Remove",
+ "admin.models.save": "Save",
+ "admin.models.saveErrorTitle": "Save error",
+ "admin.models.saveNetworkDesc": "Could not save the configuration.",
+ "admin.models.savedDesc": "The new default model applies from the next translation, without redeployment.",
+ "admin.models.savedTitle": "Models saved",
+ "admin.models.saving": "Saving...",
+ "admin.models.setDefault": "Set {model} as default model",
+ "admin.models.sharedTierNote": "Shared tier: the Business plan also uses the Essential tier for its “openrouter” engine — the list above applies to both plans.",
+ "admin.models.subtitle": "Plans → AI tiers matrix. The model actually used per translation is the plan's tier model: a Pro plan can never trigger a Premium model.",
+ "admin.models.title": "Models & subscriptions",
+ "admin.stats.aiTiers": "AI tiers (30 d)",
+ "admin.stats.includingCredits": "including {amount} in credits",
+ "admin.stats.mrr": "Estimated MRR",
+ "admin.stats.noTranslationsYet": "No translation stored in the last 30 days: the AI tier breakdown will fill in with upcoming translations.",
+ "admin.stats.payments30": "{count} payment(s) in 30 days",
+ "admin.stats.refreshing": "Refreshing...",
+ "admin.stats.revenue30": "Revenue (30 days)",
+ "admin.stats.revenueTotal": "Total collected revenue",
+ "admin.stats.tiersSub": "Essential / Premium · classic {classic} · other {other}",
+ "admin.stats.unavailable": "Business statistics unavailable ({error}).",
+ "admin.stats.waitlist": "Waitlist",
+ "admin.stats.waitlistSub": "people waiting"
}
diff --git a/frontend/src/lib/i18n/messages/en/landing.json b/frontend/src/lib/i18n/messages/en/landing.json
index cef46a9..243d12c 100644
--- a/frontend/src/lib/i18n/messages/en/landing.json
+++ b/frontend/src/lib/i18n/messages/en/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "For demanding professionals",
"landing.pricing.pro.f1": "200 documents / month",
"landing.pricing.pro.f2": "Up to 200 pages per doc",
- "landing.pricing.pro.f3": "AI-powered translation",
+ "landing.pricing.pro.f3": "Essential AI: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google included",
"landing.pricing.pro.f5": "Custom glossaries & prompts",
"landing.pricing.pro.f6": "Priority support",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "For teams with high-volume needs",
"landing.pricing.business.f1": "1,000 documents / month",
"landing.pricing.business.f2": "Up to 500 pages per doc",
- "landing.pricing.business.f3": "Premium AI (Claude)",
+ "landing.pricing.business.f3": "Premium AI (Claude 5)",
"landing.pricing.business.f4": "All providers + API access",
"landing.pricing.business.f5": "Webhooks & automation",
"landing.pricing.business.f6": "5 team seats",
diff --git a/frontend/src/lib/i18n/messages/en/pricing.json b/frontend/src/lib/i18n/messages/en/pricing.json
index 92853bb..749f622 100644
--- a/frontend/src/lib/i18n/messages/en/pricing.json
+++ b/frontend/src/lib/i18n/messages/en/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "90-day history",
"pricing.plans.business.feat1": "1,000 documents / month",
"pricing.plans.business.feat2": "Up to 500 pages per document",
- "pricing.plans.business.feat3": "Essential + Premium AI (Claude Haiku)",
+ "pricing.plans.business.feat3": "Essential AI + Premium AI (Claude 5)",
"pricing.plans.business.feat4": "All translation providers",
"pricing.plans.business.feat5": "Files up to 50 MB",
"pricing.plans.business.feat6": "API access (10,000 calls/month)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "1-year history",
"pricing.plans.business.feat10": "Advanced analytics",
"pricing.plans.enterprise.feat1": "Unlimited documents",
- "pricing.plans.enterprise.feat2": "All AI models (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "All AI models (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "On-premise or dedicated cloud deployment",
"pricing.plans.enterprise.feat4": "99.9% SLA guaranteed",
"pricing.plans.enterprise.feat5": "24/7 dedicated support",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "Ultra-fast multi-threaded AI",
"pricing.trust.availability.title": "Available 24/7",
"pricing.trust.availability.sub": "99.9% guaranteed uptime",
- "pricing.aiModels.title": "Our AI Models — March 2026",
+ "pricing.aiModels.title": "Our AI Models — September 2026",
"pricing.aiModels.essential.title": "Essential AI Translation",
"pricing.aiModels.essential.plan": "Pro Plan",
"pricing.aiModels.essential.descPrefix": "Based on",
- "pricing.aiModels.essential.descSuffix": "— the most cost-effective AI model of 2026. Quality comparable to frontier models at a fraction of the cost.",
- "pricing.aiModels.essential.modelName": "our Essential AI model",
- "pricing.aiModels.essential.context": "163K tokens of context",
- "pricing.aiModels.essential.value": "Excellent value for money",
+ "pricing.aiModels.essential.descSuffix": "— the most cost-effective AI models of 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash and MiniMax M3",
+ "pricing.aiModels.essential.context": "Up to 1.3M context (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "The best value for money",
"pricing.aiModels.premium.title": "Premium AI Translation",
"pricing.aiModels.premium.plan": "Business Plan",
"pricing.aiModels.premium.descPrefix": "Based on",
"pricing.aiModels.premium.descSuffix": "by Anthropic — accurate on legal, medical and complex technical documents.",
- "pricing.aiModels.premium.context": "200K tokens of context",
+ "pricing.aiModels.premium.context": "1M context",
"pricing.aiModels.premium.precision": "Best accuracy",
"pricing.faq.title": "Frequently asked questions",
"pricing.faq.q1": "Can I change plans at any time?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "What is \\\"Essential AI Translation\\\"?",
"pricing.faq.a2": "It's our AI engine. It understands your documents' context, preserves layout and handles technical terms much better than classic translation.",
"pricing.faq.q3": "What's the difference between Essential and Premium AI?",
- "pricing.faq.a3": "Essential AI uses an optimized model (excellent value for money). Premium AI uses Anthropic's Claude Sonnet 4.6, more accurate on legal, medical and complex technical documents.",
+ "pricing.faq.a3": "Essential AI runs on DeepSeek V4 Flash, GLM-5.3 Flash and MiniMax M3 (excellent value for money). Premium AI uses Anthropic's Claude 5, more accurate on legal, medical and complex technical documents.",
"pricing.faq.q4": "Are my documents kept after translation?",
"pricing.faq.a4": "Translated files are available according to your plan (30 days Starter, 90 days Pro, 1 year Business). They are encrypted at rest and in transit.",
"pricing.faq.q5": "What happens if I exceed my monthly quota?",
@@ -163,5 +163,9 @@
"pricing.freeBand.text": "Just want to try? Start free — 5 documents per month, no card required.",
"pricing.freeBand.cta": "Start free",
"pricing.enterpriseBand.text": "Volume, dedicated engines, on-premise options — let's talk.",
- "pricing.enterpriseBand.cta": "Contact us"
+ "pricing.enterpriseBand.cta": "Contact us",
+ "pricing.aiModels.essential.price": "From $0.09 per million tokens",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Alternatives: DeepSeek V4 Pro and GLM-5.3",
+ "pricing.aiModels.premium.price": "$2 / $10 per 1M tokens"
}
diff --git a/frontend/src/lib/i18n/messages/en/providerTheme.json b/frontend/src/lib/i18n/messages/en/providerTheme.json
index c99d006..4f8db90 100644
--- a/frontend/src/lib/i18n/messages/en/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/en/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Unified access to the best open-source models optimized for translation.",
"providerTheme.openrouter_premium.badge": "Ultra",
"providerTheme.openrouter_premium.subBadge": "Maximum context",
- "providerTheme.openrouter_premium.desc": "Assisted by state-of-the-art models (GPT-4o, Claude Sonnet 4.6) for long documents.",
+ "providerTheme.openrouter_premium.desc": "Assisted by state-of-the-art models (GPT-4o, Claude 5) for long documents.",
"providerTheme.zai.badge": "Specialized",
"providerTheme.zai.subBadge": "Finance & Law",
"providerTheme.zai.desc": "Model fine-tuned for demanding business terminologies (legal, finance).",
diff --git a/frontend/src/lib/i18n/messages/es/admin.json b/frontend/src/lib/i18n/messages/es/admin.json
index 5fae644..0582ad4 100644
--- a/frontend/src/lib/i18n/messages/es/admin.json
+++ b/frontend/src/lib/i18n/messages/es/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Proveedores",
"admin.nav.system": "Sistema",
"admin.nav.logs": "Registros",
+ "admin.nav.stats": "Estadísticas",
"admin.users.title": "Gestión de Usuarios",
"admin.users.subtitle": "Ver y gestionar cuentas de usuario",
"admin.users.planUpdated": "Plan actualizado",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "Esperando datos...",
"admin.system.purging": "Purgando...",
"admin.system.clean": "Limpiar",
- "admin.system.purge": "Purgar"
+ "admin.system.purge": "Purgar",
+ "admin.nav.models": "Modelos y suscripciones",
+ "admin.nav.marketing": "Marketing",
+ "admin.marketing.audience": "Audiencia",
+ "admin.marketing.audienceAllUsers": "Todas las cuentas",
+ "admin.marketing.audienceInactive": "Cuentas inactivas desde hace 30 días",
+ "admin.marketing.audiencePlaceholder": "Elegir una audiencia",
+ "admin.marketing.audiencePlanBusiness": "Plan Business",
+ "admin.marketing.audiencePlanEnterprise": "Plan Enterprise",
+ "admin.marketing.audiencePlanFree": "Plan Gratuito",
+ "admin.marketing.audiencePlanPro": "Plan Pro",
+ "admin.marketing.audiencePlanStarter": "Plan Starter",
+ "admin.marketing.audienceWaitlist": "Lista de espera",
+ "admin.marketing.badgeReal": "real",
+ "admin.marketing.badgeTest": "prueba",
+ "admin.marketing.confirmSend": "¿Enviar este correo a {count} destinatario(s) («{audience}»)?",
+ "admin.marketing.footerNote": "Un pie de página con el enlace de baja se añade automáticamente al enviar.",
+ "admin.marketing.hidePreview": "Ocultar vista previa",
+ "admin.marketing.history": "Historial de envíos",
+ "admin.marketing.historyFailedSuffix": ", {count} fallos",
+ "admin.marketing.historySent": "{sent}/{total} enviados",
+ "admin.marketing.html": "Contenido HTML",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Rellena el asunto y el HTML.",
+ "admin.marketing.incompleteTitle": "Contenido incompleto",
+ "admin.marketing.loadErrorTitle": "Error de carga",
+ "admin.marketing.localCheckUnavailable": "Verificación local no disponible en este navegador: el servidor controlará el envío de prueba previo.",
+ "admin.marketing.networkDesc": "No se puede contactar con el backend.",
+ "admin.marketing.newCampaign": "Nuevo envío",
+ "admin.marketing.noHistory": "Ningún envío registrado.",
+ "admin.marketing.preview": "Vista previa",
+ "admin.marketing.previewEmpty": "Vista previa: pega tu HTML al lado.",
+ "admin.marketing.previewTitle": "Vista previa del correo",
+ "admin.marketing.recipientsAvailable": "{count} destinatario(s) disponibles tras excluir las bajas.",
+ "admin.marketing.sendReal": "Enviar a {count} destinatario(s)",
+ "admin.marketing.sendRefusedTitle": "Envío rechazado",
+ "admin.marketing.sendTest": "Envío de prueba",
+ "admin.marketing.sendingDesc": "{count} correo(s) en cola de envío (intervalo de 0,2 s). Las bajas quedan excluidas.",
+ "admin.marketing.sendingTitle": "Envío en curso",
+ "admin.marketing.subject": "Asunto",
+ "admin.marketing.subjectPlaceholder": "Ej.: Tu traducción te espera — 20 % durante 7 días",
+ "admin.marketing.subtitle": "Envío de correos a una audiencia, con envío de prueba obligatorio, enlace de baja automático e historial completo.",
+ "admin.marketing.testDoneDesc": "Correo recibido en {email}",
+ "admin.marketing.testDoneTitle": "Envío de prueba realizado",
+ "admin.marketing.testEmail": "Correo de prueba (opcional)",
+ "admin.marketing.testEmailPlaceholder": "Si no: dirección remitente SMTP",
+ "admin.marketing.testFailedTitle": "Fallo del envío de prueba",
+ "admin.marketing.testRequiredHint": "El envío real exige un envío de prueba previo de este contenido exacto (verificado por el servidor).",
+ "admin.marketing.testValidated": "Envío de prueba validado para este contenido.",
+ "admin.marketing.title": "Marketing — envíos de correo",
+ "admin.marketing.unsubscribedNote": "{count} contacto(s) de baja serán excluidos de todo envío.",
+ "admin.models.addModel": "Añadir un modelo",
+ "admin.models.businessBadge": "Plan Business",
+ "admin.models.cancel": "Cancelar",
+ "admin.models.catalog": "Catálogo de OpenRouter",
+ "admin.models.catalogErrorDesc": "No se pudo cargar el catálogo de OpenRouter.",
+ "admin.models.catalogErrorTitle": "Catálogo no disponible",
+ "admin.models.customBadge": "Nivel personalizado",
+ "admin.models.customNote": "Modelos a medida, definidos caso por caso con el cliente.",
+ "admin.models.defaultBadge": "por defecto",
+ "admin.models.emptyTier": "Sin modelos: se usará la gama oficial del plan.",
+ "admin.models.enterpriseBadge": "Plan Enterprise",
+ "admin.models.essentialBadge": "Nivel de IA Esencial",
+ "admin.models.essentialCost": "— facturado con factor de coste 1",
+ "admin.models.fallbackFirst": "(respaldo n.º 1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — comprueba tu token de administrador.",
+ "admin.models.loadErrorTitle": "Error de carga",
+ "admin.models.matrixDesc": "Modelos activos por nivel — el orden de la lista es la prioridad de respaldo. El botón de opción elige el modelo por defecto, aplicado al instante sin redespliegue.",
+ "admin.models.matrixTitle": "Matriz de modelos por plan",
+ "admin.models.missingConfig": "Configuración de niveles de IA no encontrada. Recarga la página.",
+ "admin.models.moveDown": "Bajar",
+ "admin.models.moveUp": "Subir",
+ "admin.models.networkDesc": "No se puede contactar con el backend.",
+ "admin.models.networkTitle": "Error de red",
+ "admin.models.premiumBadge": "Nivel de IA Premium",
+ "admin.models.premiumCost": "— facturado con factor de coste 5",
+ "admin.models.premiumReservedNote": "Reservado a los planes Business y Enterprise (motor «openrouter_premium»).",
+ "admin.models.proBadge": "Plan Pro",
+ "admin.models.remove": "Quitar",
+ "admin.models.save": "Guardar",
+ "admin.models.saveErrorTitle": "Error al guardar",
+ "admin.models.saveNetworkDesc": "No se pudo guardar la configuración.",
+ "admin.models.savedDesc": "El nuevo modelo por defecto se usa desde la siguiente traducción, sin redespliegue.",
+ "admin.models.savedTitle": "Modelos guardados",
+ "admin.models.saving": "Guardando...",
+ "admin.models.setDefault": "Definir {model} como modelo por defecto",
+ "admin.models.sharedTierNote": "Nivel compartido: el plan Business también usa el nivel Esencial con su motor «openrouter» — la lista de arriba se aplica a ambos planes.",
+ "admin.models.subtitle": "Matriz planes → niveles de IA. El modelo realmente usado en cada traducción es el del nivel del plan: un plan Pro nunca puede disparar un modelo Premium.",
+ "admin.models.title": "Modelos y suscripciones",
+ "admin.stats.aiTiers": "Niveles de IA (30 d)",
+ "admin.stats.includingCredits": "incluidos {amount} en créditos",
+ "admin.stats.mrr": "MRR estimado",
+ "admin.stats.noTranslationsYet": "Sin traducciones registradas en los últimos 30 días: el reparto por niveles se completará con las próximas traducciones.",
+ "admin.stats.payments30": "{count} pago(s) en 30 días",
+ "admin.stats.refreshing": "Actualizando...",
+ "admin.stats.revenue30": "Ingresos (30 días)",
+ "admin.stats.revenueTotal": "Ingresos cobrados (total)",
+ "admin.stats.tiersSub": "Esencial / Premium · clásico {classic} · otro {other}",
+ "admin.stats.unavailable": "Estadísticas de negocio no disponibles ({error}).",
+ "admin.stats.waitlist": "Lista de espera",
+ "admin.stats.waitlistSub": "inscritos en espera"
}
diff --git a/frontend/src/lib/i18n/messages/es/landing.json b/frontend/src/lib/i18n/messages/es/landing.json
index c301cfc..6a986bc 100644
--- a/frontend/src/lib/i18n/messages/es/landing.json
+++ b/frontend/src/lib/i18n/messages/es/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "Para profesionales exigentes",
"landing.pricing.pro.f1": "200 documentos / mes",
"landing.pricing.pro.f2": "Hasta 200 páginas por doc",
- "landing.pricing.pro.f3": "Traducción con IA",
+ "landing.pricing.pro.f3": "IA Esencial: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google incluidos",
"landing.pricing.pro.f5": "Glosarios y prompts personalizados",
"landing.pricing.pro.f6": "Soporte prioritario",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "Para equipos con grandes necesidades",
"landing.pricing.business.f1": "1 000 documentos / mes",
"landing.pricing.business.f2": "Hasta 500 páginas por doc",
- "landing.pricing.business.f3": "IA Premium (Claude)",
+ "landing.pricing.business.f3": "IA Premium (Claude 5)",
"landing.pricing.business.f4": "Todos los proveedores + API",
"landing.pricing.business.f5": "Webhooks y automatización",
"landing.pricing.business.f6": "5 puestos de equipo",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "Archivos DOCX, XLSX, PPTX o PDF compatibles",
"landing.translate.aiAnalysis": "Análisis IA Activo",
"landing.translate.processing": "Procesando",
- "landing.translate.preservingLayout": "Tu diseño se está preservando"
+ "landing.translate.preservingLayout": "Tu diseño se está preservando",
+ "landing.beforeAfter.seal": "El mismo diseño, palabra por palabra",
+ "landing.beforeAfter.proof1": "Los diagramas SmartArt se reconstruyen, no se aplanan",
+ "landing.beforeAfter.proof2": "Series y ejes de los gráficos traducidos",
+ "landing.beforeAfter.proof3": "Índices regenerados en el idioma de destino",
+ "landing.beforeAfter.targetTitle": "Pliego técnico — Tratamiento de aire",
+ "landing.beforeAfter.targetPara1": "La instalación de ventilación debe ponerse en servicio antes de finales del segundo trimestre.",
+ "landing.beforeAfter.targetPara2Before": "La",
+ "landing.beforeAfter.targetTerm": "unidad de tratamiento de aire",
+ "landing.beforeAfter.targetPara2After": "debe cumplir la clase de filtro F7 según el calendario.",
+ "landing.beforeAfter.targetItem1": "Carga térmica: 42 kW a caudal nominal",
+ "landing.beforeAfter.targetItem2": "Nivel sonoro inferior a 45 dB(A) a 3 m",
+ "landing.formats.pill": "COMPATIBILIDAD",
+ "landing.hero.visualCaption": "El mismo diseño, otro idioma — nada más cambia",
+ "landing.pricing.free.name": "Gratis",
+ "landing.pricing.free.desc": "Ideal para descubrir la aplicación",
+ "landing.pricing.free.cta": "Elegir este plan",
+ "landing.pricing.enterprise.name": "Empresas",
+ "landing.pricing.enterprise.desc": "Soluciones a medida para grandes organizaciones",
+ "landing.pricing.enterprise.cta": "Contáctenos",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/es/pricing.json b/frontend/src/lib/i18n/messages/es/pricing.json
index d774d7a..978630c 100644
--- a/frontend/src/lib/i18n/messages/es/pricing.json
+++ b/frontend/src/lib/i18n/messages/es/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "Historial de 90 días",
"pricing.plans.business.feat1": "1 000 documentos / mes",
"pricing.plans.business.feat2": "Hasta 500 páginas por documento",
- "pricing.plans.business.feat3": "IA Esencial + Premium (Claude Haiku)",
+ "pricing.plans.business.feat3": "IA Esencial + IA Premium (Claude 5)",
"pricing.plans.business.feat4": "Todos los proveedores de traducción",
"pricing.plans.business.feat5": "Archivos de hasta 50 MB",
"pricing.plans.business.feat6": "Acceso API (10 000 llamadas/mes)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "Historial de 1 año",
"pricing.plans.business.feat10": "Analíticas avanzadas",
"pricing.plans.enterprise.feat1": "Documentos ilimitados",
- "pricing.plans.enterprise.feat2": "Todos los modelos de IA (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "Todos los modelos de IA (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "Implementación local o en nube dedicada",
"pricing.plans.enterprise.feat4": "SLA 99,9 % garantizado",
"pricing.plans.enterprise.feat5": "Soporte dedicado 24/7",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "IA multiproceso ultrarrápida",
"pricing.trust.availability.title": "Disponible 24/7",
"pricing.trust.availability.sub": "Disponibilidad garantizada del 99,9 %",
- "pricing.aiModels.title": "Nuestros modelos de IA — Marzo 2026",
+ "pricing.aiModels.title": "Nuestros modelos de IA — Septiembre 2026",
"pricing.aiModels.essential.title": "Traducción IA Esencial",
"pricing.aiModels.essential.plan": "Plan Pro",
"pricing.aiModels.essential.descPrefix": "Basado en",
- "pricing.aiModels.essential.descSuffix": "— el modelo de IA más rentable de 2026. Calidad comparable a los modelos frontier a una fracción del coste.",
- "pricing.aiModels.essential.modelName": "nuestro modelo IA Esencial",
- "pricing.aiModels.essential.context": "163K tokens de contexto",
- "pricing.aiModels.essential.value": "Excelente relación calidad-precio",
+ "pricing.aiModels.essential.descSuffix": "— los modelos de IA con la mejor relación calidad-precio de 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash y MiniMax M3",
+ "pricing.aiModels.essential.context": "Hasta 1,3 M de contexto (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "La mejor relación calidad-precio",
"pricing.aiModels.premium.title": "Traducción IA Premium",
"pricing.aiModels.premium.plan": "Plan Business",
"pricing.aiModels.premium.descPrefix": "Basado en",
"pricing.aiModels.premium.descSuffix": "de Anthropic — preciso en documentos jurídicos, médicos y técnicos complejos.",
- "pricing.aiModels.premium.context": "200K tokens de contexto",
+ "pricing.aiModels.premium.context": "1M de contexto",
"pricing.aiModels.premium.precision": "Máxima precisión",
"pricing.faq.title": "Preguntas frecuentes",
"pricing.faq.q1": "¿Puedo cambiar de plan en cualquier momento?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "¿Qué es la «Traducción IA Esencial»?",
"pricing.faq.a2": "Es nuestro motor de IA. Comprende el contexto de tus documentos, conserva el diseño y maneja términos técnicos mucho mejor que una traducción clásica.",
"pricing.faq.q3": "¿Cuál es la diferencia entre IA Esencial e IA Premium?",
- "pricing.faq.a3": "La IA Esencial usa un modelo optimizado (excelente relación calidad-precio). La IA Premium usa Claude 3.5 Haiku de Anthropic, más precisa en documentos jurídicos, médicos y técnicos complejos.",
+ "pricing.faq.a3": "La IA Esencial se apoya en DeepSeek V4 Flash, GLM-5.3 Flash y MiniMax M3 (excelente relación calidad-precio). La IA Premium usa Claude 5 de Anthropic, más precisa en documentos jurídicos, médicos y técnicos complejos.",
"pricing.faq.q4": "¿Se conservan mis documentos después de la traducción?",
"pricing.faq.a4": "Los archivos traducidos están disponibles según tu plan (30 días Starter, 90 días Pro, 1 año Business). Están cifrados en reposo y en tránsito.",
"pricing.faq.q5": "¿Qué ocurre si supero mi cuota mensual?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "Error al crear el pago.",
"pricing.dashboard": "Panel",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "Desde 0,09 $ por millón de tokens",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Alternativas: DeepSeek V4 Pro y GLM-5.3",
+ "pricing.confirm.title": "Confirma tu suscripción",
+ "pricing.confirm.subtitle": "Serás redirigido a nuestro proveedor de pago seguro.",
+ "pricing.confirm.cancel": "Cancelar",
+ "pricing.confirm.cta": "Continuar al pago",
+ "pricing.confirm.year": "año",
+ "pricing.confirm.month": "mes",
+ "pricing.confirm.monthlyEquivalent": "Facturación anual — equivalente a {price} € / mes.",
+ "pricing.confirm.secureNote": "Cancelable en cualquier momento desde tu perfil. El pago lo procesa Stripe; los datos de tu tarjeta nunca pasan por nuestros servidores.",
+ "pricing.enterprise.subject": "Consulta Enterprise",
+ "pricing.enterpriseBand.cta": "Contáctenos",
+ "pricing.enterpriseBand.text": "Volumen, motores dedicados, opciones on-premise — hablemos.",
+ "pricing.error.server": "Error del servidor {status}",
+ "pricing.freeBand.cta": "Empieza gratis",
+ "pricing.freeBand.text": "¿Solo quieres probar? Empieza gratis — 5 documentos al mes, sin tarjeta.",
+ "pricing.header.titleBase": "Un plan para",
+ "pricing.header.titleAccent": "cada necesidad",
+ "pricing.toast.close": "Cerrar",
+ "pricing.aiModels.premium.price": "2 $ / 10 $ por 1M de tokens"
}
diff --git a/frontend/src/lib/i18n/messages/es/providerTheme.json b/frontend/src/lib/i18n/messages/es/providerTheme.json
index 1ed68fc..5a6b074 100644
--- a/frontend/src/lib/i18n/messages/es/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/es/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Acceso unificado a los mejores modelos de código abierto optimizados para traducción.",
"providerTheme.openrouter_premium.badge": "Ultra",
"providerTheme.openrouter_premium.subBadge": "Contexto máximo",
- "providerTheme.openrouter_premium.desc": "Asistido por modelos de última generación (GPT-4o, Claude Sonnet 4.6) para documentos largos.",
+ "providerTheme.openrouter_premium.desc": "Asistido por modelos de última generación (GPT-4o, Claude 5) para documentos largos.",
"providerTheme.zai.badge": "Especializada",
"providerTheme.zai.subBadge": "Finanzas y Derecho",
"providerTheme.zai.desc": "Modelo ajustado para terminologías empresariales exigentes (legal, finanzas).",
diff --git a/frontend/src/lib/i18n/messages/fa/admin.json b/frontend/src/lib/i18n/messages/fa/admin.json
index 3eb361a..9afe0dd 100644
--- a/frontend/src/lib/i18n/messages/fa/admin.json
+++ b/frontend/src/lib/i18n/messages/fa/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "ارائهدهندگان",
"admin.nav.system": "سیستم",
"admin.nav.logs": "گزارشها",
+ "admin.nav.stats": "آمار",
"admin.users.title": "مدیریت کاربران",
"admin.users.subtitle": "مشاهده و مدیریت حسابهای کاربری",
"admin.users.planUpdated": "طرح بهروز شد",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "در انتظار داده...",
"admin.system.purging": "در حال پاکسازی...",
"admin.system.clean": "پاکسازی",
- "admin.system.purge": "حذف کامل"
+ "admin.system.purge": "حذف کامل",
+ "admin.nav.models": "مدلها و اشتراکها",
+ "admin.nav.marketing": "بازاریابی",
+ "admin.marketing.audience": "مخاطب",
+ "admin.marketing.audienceAllUsers": "همه حسابها",
+ "admin.marketing.audienceInactive": "حسابهای غیرفعال از ۳۰ روز پیش",
+ "admin.marketing.audiencePlaceholder": "انتخاب مخاطب",
+ "admin.marketing.audiencePlanBusiness": "اشتراک Business",
+ "admin.marketing.audiencePlanEnterprise": "اشتراک Enterprise",
+ "admin.marketing.audiencePlanFree": "اشتراک رایگان",
+ "admin.marketing.audiencePlanPro": "اشتراک Pro",
+ "admin.marketing.audiencePlanStarter": "اشتراک Starter",
+ "admin.marketing.audienceWaitlist": "فهرست انتظار",
+ "admin.marketing.badgeReal": "واقعی",
+ "admin.marketing.badgeTest": "آزمایشی",
+ "admin.marketing.confirmSend": "این ایمیل به {count} گیرنده ارسال شود («{audience}»)؟",
+ "admin.marketing.footerNote": "یک پانوشت با پیوند لغو اشتراک بهطور خودکار به ارسال افزوده میشود.",
+ "admin.marketing.hidePreview": "پنهان کردن پیشنمایش",
+ "admin.marketing.history": "تاریخچه ارسالها",
+ "admin.marketing.historyFailedSuffix": "، {count} ناموفق",
+ "admin.marketing.historySent": "{sent}/{total} ارسالشده",
+ "admin.marketing.html": "محتوای HTML",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "موضوع و HTML را پر کنید.",
+ "admin.marketing.incompleteTitle": "محتوای ناقص",
+ "admin.marketing.loadErrorTitle": "خطا در بارگذاری",
+ "admin.marketing.localCheckUnavailable": "بررسی محلی در این مرورگر در دسترس نیست: سرور ارسال آزمایشی قبلی را کنترل خواهد کرد.",
+ "admin.marketing.networkDesc": "دسترسی به بکاند ممکن نیست.",
+ "admin.marketing.newCampaign": "کمپین جدید",
+ "admin.marketing.noHistory": "هیچ ارسالی ثبت نشده است.",
+ "admin.marketing.preview": "پیشنمایش",
+ "admin.marketing.previewEmpty": "پیشنمایش: HTML خود را در کنار این بوم بچسبانید.",
+ "admin.marketing.previewTitle": "پیشنمایش ایمیل",
+ "admin.marketing.recipientsAvailable": "{count} گیرنده پس از کنارگذاشتن لغواشتراکها در دسترس است.",
+ "admin.marketing.sendReal": "ارسال به {count} گیرنده",
+ "admin.marketing.sendRefusedTitle": "ارسال رد شد",
+ "admin.marketing.sendTest": "ارسال آزمایشی",
+ "admin.marketing.sendingDesc": "{count} ایمیل در صف ارسال (فاصله ۰٫۲ ثانیه). لغواشتراکها مستثنی هستند.",
+ "admin.marketing.sendingTitle": "ارسال در جریان است",
+ "admin.marketing.subject": "موضوع",
+ "admin.marketing.subjectPlaceholder": "مثال: ترجمه شما منتظر است — ۲۰٪ به مدت ۷ روز",
+ "admin.marketing.subtitle": "ارسال ایمیل به یک مخاطب، با ارسال آزمایشی الزامی، پیوند لغو اشتراک خودکار و تاریخچه کامل.",
+ "admin.marketing.testDoneDesc": "ایمیل به {email} رسید",
+ "admin.marketing.testDoneTitle": "ارسال آزمایشی انجام شد",
+ "admin.marketing.testEmail": "ایمیل آزمایشی (اختیاری)",
+ "admin.marketing.testEmailPlaceholder": "در غیر این صورت: آدرس فرستنده SMTP",
+ "admin.marketing.testFailedTitle": "ارسال آزمایشی ناموفق بود",
+ "admin.marketing.testRequiredHint": "ارسال واقعی نیازمند ارسال آزمایشی قبلی همین محتوا است (تأییدشده توسط سرور).",
+ "admin.marketing.testValidated": "ارسال آزمایشی این محتوا تأیید شده است.",
+ "admin.marketing.title": "بازاریابی — کمپینهای ایمیلی",
+ "admin.marketing.unsubscribedNote": "{count} لغواشتراک از هر ارسالی مستثنی خواهند شد.",
+ "admin.models.addModel": "افزودن مدل",
+ "admin.models.businessBadge": "اشتراک Business",
+ "admin.models.cancel": "لغو",
+ "admin.models.catalog": "کاتالوگ OpenRouter",
+ "admin.models.catalogErrorDesc": "بارگذاری کاتالوگ OpenRouter ممکن نشد.",
+ "admin.models.catalogErrorTitle": "کاتالوگ در دسترس نیست",
+ "admin.models.customBadge": "سطح سفارشی",
+ "admin.models.customNote": "مدلهای سفارشی، مورد به مورد با مشتری توافق میشود.",
+ "admin.models.defaultBadge": "پیشفرض",
+ "admin.models.emptyTier": "بدون مدل: مجموعه رسمی اشتراک استفاده خواهد شد.",
+ "admin.models.enterpriseBadge": "اشتراک Enterprise",
+ "admin.models.essentialBadge": "سطح هوش مصنوعی پایه",
+ "admin.models.essentialCost": "— محاسبه با ضریب هزینه ۱",
+ "admin.models.fallbackFirst": "(پشتیبان شماره ۱)",
+ "admin.models.loadErrorDesc": "HTTP {status} — توکن مدیر را بررسی کنید.",
+ "admin.models.loadErrorTitle": "خطا در بارگذاری",
+ "admin.models.matrixDesc": "مدلهای فعال هر سطح — ترتیب فهرست، اولویت پشتیبان است. دکمه رادیویی مدل پیشفرض را انتخاب میکند که بلافاصله و بدون استقرار مجدد اعمال میشود.",
+ "admin.models.matrixTitle": "ماتریس مدلها به تفکیک اشتراک",
+ "admin.models.missingConfig": "پیکربندی سطوح هوش مصنوعی یافت نشد. صفحه را دوباره بارگذاری کنید.",
+ "admin.models.moveDown": "انتقال به پایین",
+ "admin.models.moveUp": "انتقال به بالا",
+ "admin.models.networkDesc": "دسترسی به بکاند ممکن نیست.",
+ "admin.models.networkTitle": "خطای شبکه",
+ "admin.models.premiumBadge": "سطح هوش مصنوعی پیشرفته",
+ "admin.models.premiumCost": "— محاسبه با ضریب هزینه ۵",
+ "admin.models.premiumReservedNote": "مخصوص اشتراکهای Business و Enterprise (موتور «openrouter_premium»).",
+ "admin.models.proBadge": "اشتراک Pro",
+ "admin.models.remove": "حذف",
+ "admin.models.save": "ذخیره",
+ "admin.models.saveErrorTitle": "خطا در ذخیره",
+ "admin.models.saveNetworkDesc": "ذخیره پیکربندی ممکن نشد.",
+ "admin.models.savedDesc": "مدل پیشفرض جدید از ترجمه بعدی استفاده میشود، بدون استقرار مجدد.",
+ "admin.models.savedTitle": "مدلها ذخیره شدند",
+ "admin.models.saving": "در حال ذخیره...",
+ "admin.models.setDefault": "تنظیم {model} بهعنوان مدل پیشفرض",
+ "admin.models.sharedTierNote": "سطح مشترک: اشتراک Business نیز برای موتور «openrouter» از سطح پایه استفاده میکند — فهرست بالا برای هر دو اشتراک اعمال میشود.",
+ "admin.models.subtitle": "ماتریس plans → سطوح هوش مصنوعی. مدل واقعی استفادهشده در هر ترجمه، مدل سطحِ همان اشتراک است: اشتراک Pro هرگز نمیتواند مدل Premium را فعال کند.",
+ "admin.models.title": "مدلها و اشتراکها",
+ "admin.stats.aiTiers": "سطوح هوش مصنوعی (۳۰ روز)",
+ "admin.stats.includingCredits": "شامل {amount} اعتبار",
+ "admin.stats.mrr": "درآمد ماهانه تکرارشونده تخمینی",
+ "admin.stats.noTranslationsYet": "هیچ ترجمهای در ۳۰ روز گذشته ثبت نشده است: تفکیک سطوح با ترجمههای بعدی کامل میشود.",
+ "admin.stats.payments30": "{count} پرداخت در ۳۰ روز",
+ "admin.stats.refreshing": "در حال بهروزرسانی...",
+ "admin.stats.revenue30": "درآمد (۳۰ روز)",
+ "admin.stats.revenueTotal": "کل درآمد دریافتشده",
+ "admin.stats.tiersSub": "پایه / پیشرفته · کلاسیک {classic} · سایر {other}",
+ "admin.stats.unavailable": "آمار کسبوکار در دسترس نیست ({error}).",
+ "admin.stats.waitlist": "فهرست انتظار",
+ "admin.stats.waitlistSub": "نفر در انتظار"
}
diff --git a/frontend/src/lib/i18n/messages/fa/landing.json b/frontend/src/lib/i18n/messages/fa/landing.json
index fedf8c4..c647382 100644
--- a/frontend/src/lib/i18n/messages/fa/landing.json
+++ b/frontend/src/lib/i18n/messages/fa/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "برای متخصصان حرفهای",
"landing.pricing.pro.f1": "۲۰۰ سند / ماه",
"landing.pricing.pro.f2": "تا ۲۰۰ صفحه برای هر سند",
- "landing.pricing.pro.f3": "ترجمه مبتنی بر هوش مصنوعی",
+ "landing.pricing.pro.f3": "هوش مصنوعی پایه: DeepSeek V4 Flash، GLM-5.3 Flash، MiniMax M3",
"landing.pricing.pro.f4": "Google شامل است",
"landing.pricing.pro.f5": "واژهنامه و پرامپت سفارشی",
"landing.pricing.pro.f6": "پشتیبانی اولویتدار",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "برای تیمها با نیاز بالا",
"landing.pricing.business.f1": "۱,۰۰۰ سند / ماه",
"landing.pricing.business.f2": "تا ۵۰۰ صفحه برای هر سند",
- "landing.pricing.business.f3": "هوش مصنوعی پیشرفته (Claude)",
+ "landing.pricing.business.f3": "هوش مصنوعی پیشرفته (Claude 5)",
"landing.pricing.business.f4": "همه ارائهدهندگان + API",
"landing.pricing.business.f5": "وبهوک و خودکارسازی",
"landing.pricing.business.f6": "۵ صندلی تیمی",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "فایلهای DOCX, XLSX, PPTX یا PDF پشتیبانی میشوند",
"landing.translate.aiAnalysis": "تحلیل AI فعال",
"landing.translate.processing": "در حال پردازش",
- "landing.translate.preservingLayout": "طرحبندی شما حفظ میشود"
+ "landing.translate.preservingLayout": "طرحبندی شما حفظ میشود",
+ "landing.beforeAfter.seal": "همان چیدمان، کلمه به کلمه",
+ "landing.beforeAfter.proof1": "نمودارهای SmartArt بازسازی میشوند، نه تختشده",
+ "landing.beforeAfter.proof2": "سریها و محورهای نمودار ترجمه میشوند",
+ "landing.beforeAfter.proof3": "فهرست مطالب به زبان مقصد بازسازی میشود",
+ "landing.beforeAfter.targetTitle": "مشخصات فنی — تهویه هوا",
+ "landing.beforeAfter.targetPara1": "سیستم تهویه باید پیش از پایان سهماهه دوم راهاندازی شود.",
+ "landing.beforeAfter.targetPara2Before": "",
+ "landing.beforeAfter.targetTerm": "هواساز مرکزی",
+ "landing.beforeAfter.targetPara2After": "باید مطابق برنامهریزی کلاس فیلتر F7 را رعایت کند.",
+ "landing.beforeAfter.targetItem1": "بار حرارتی: ۴۲ کیلووات در جریان اسمی",
+ "landing.beforeAfter.targetItem2": "سطح صدا کمتر از ۴۵ دسیبل (A) در فاصله ۳ متری",
+ "landing.formats.pill": "سازگاری",
+ "landing.hero.visualCaption": "همان چیدمان، زبان جدید — هیچ چیز دیگری تغییر نمیکند",
+ "landing.pricing.free.name": "رایگان",
+ "landing.pricing.free.desc": "برای آشنایی با اپلیکیشن ایدهآل است",
+ "landing.pricing.free.cta": "انتخاب این برنامه",
+ "landing.pricing.enterprise.name": "سازمانی",
+ "landing.pricing.enterprise.desc": "راهکارهای سفارشی برای سازمانهای بزرگ",
+ "landing.pricing.enterprise.cta": "تماس با ما",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/fa/pricing.json b/frontend/src/lib/i18n/messages/fa/pricing.json
index e9148f1..f899c14 100644
--- a/frontend/src/lib/i18n/messages/fa/pricing.json
+++ b/frontend/src/lib/i18n/messages/fa/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "تاریخچه ۹۰ روزه",
"pricing.plans.business.feat1": "۱,۰۰۰ سند / ماه",
"pricing.plans.business.feat2": "تا ۵۰۰ صفحه برای هر سند",
- "pricing.plans.business.feat3": "هوش مصنوعی پایه + پیشرفته (Claude Haiku)",
+ "pricing.plans.business.feat3": "هوش مصنوعی پایه + پیشرفته (Claude 5)",
"pricing.plans.business.feat4": "تمام ارائهدهندههای ترجمه",
"pricing.plans.business.feat5": "فایلها تا ۵۰ مگابایت",
"pricing.plans.business.feat6": "دسترسی API (۱۰,۰۰۰ فراخوان/ماه)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "تاریخچه ۱ ساله",
"pricing.plans.business.feat10": "تحلیل پیشرفته",
"pricing.plans.enterprise.feat1": "اسناد نامحدود",
- "pricing.plans.enterprise.feat2": "تمام مدلهای هوش مصنوعی (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "تمام مدلهای هوش مصنوعی (Claude 5، DeepSeek V4 Pro، GLM-5.3…)",
"pricing.plans.enterprise.feat3": "استقرار محلی یا ابری اختصاصی",
"pricing.plans.enterprise.feat4": "SLA 99.9% تضمینشده",
"pricing.plans.enterprise.feat5": "پشتیبانی اختصاصی ۲۴/۷",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "هوش مصنوعی چندنخی فوقسریع",
"pricing.trust.availability.title": "۲۴/۷ در دسترس",
"pricing.trust.availability.sub": "۹۹.۹٪ زمان فعالیت تضمینشده",
- "pricing.aiModels.title": "مدلهای هوش مصنوعی ما — مارس ۲۰۲۶",
+ "pricing.aiModels.title": "مدلهای هوش مصنوعی ما — سپتامبر ۲۰۲۶",
"pricing.aiModels.essential.title": "ترجمه هوش مصنوعی پایه",
"pricing.aiModels.essential.plan": "طرح حرفهای",
"pricing.aiModels.essential.descPrefix": "مبتنی بر",
- "pricing.aiModels.essential.descSuffix": "— مقرونبهصرفهترین مدل هوش مصنوعی سال ۲۰۲۶. کیفیت قابل مقایسه با مدلهای پیشرو با کسر هزینه.",
- "pricing.aiModels.essential.modelName": "مدل هوش مصنوعی Essential",
- "pricing.aiModels.essential.context": "۱۶۳هزار توکن زمینه",
- "pricing.aiModels.essential.value": "ارزش عالی نسبت به قیمت",
+ "pricing.aiModels.essential.descSuffix": "— مقرونبهصرفهترین مدلهای هوش مصنوعی سال ۲۰۲۶.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash، GLM-5.3 Flash و MiniMax M3",
+ "pricing.aiModels.essential.context": "زمینه تا ۱٫۳ میلیون توکن (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "بهترین نسبت کیفیت به قیمت",
"pricing.aiModels.premium.title": "ترجمه هوش مصنوعی پیشرفته",
"pricing.aiModels.premium.plan": "طرح سازمانی",
"pricing.aiModels.premium.descPrefix": "مبتنی بر",
"pricing.aiModels.premium.descSuffix": "توسط Anthropic — دقیق در اسناد حقوقی، پزشکی و فنی پیچیده.",
- "pricing.aiModels.premium.context": "۲۰۰هزار توکن زمینه",
+ "pricing.aiModels.premium.context": "یک میلیون توکن زمینه",
"pricing.aiModels.premium.precision": "بالاترین دقت",
"pricing.faq.title": "سوالات متداول",
"pricing.faq.q1": "آیا میتوانم طرح را هر زمان بخواهم تغییر دهم؟",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "\\\"ترجمه هوش مصنوعی پایه\\\" چیست؟",
"pricing.faq.a2": "این موتور هوش مصنوعی ماست. زمینه اسناد شما را درک میکند، طرحبندی را حفظ میکند و اصطلاحات فنی را بسیار بهتر از ترجمه کلاسیک مدیریت میکند.",
"pricing.faq.q3": "تفاوت هوش مصنوعی پایه و پیشرفته چیست؟",
- "pricing.faq.a3": "هوش مصنوعی Essential از یک مدل بهینهشده استفاده میکند (ارزش عالی برای پول). هوش مصنوعی Premium از Claude 3.5 Haiku انترپیک استفاده میکند که در اسناد حقوقی، پزشکی و فنی پیچیده دقیقتر است.",
+ "pricing.faq.a3": "هوش مصنوعی Essential از مدلهای DeepSeek V4 Flash، GLM-5.3 Flash و MiniMax M3 استفاده میکند (ارزش عالی برای پول). هوش مصنوعی Premium از Claude 5 انترپیک استفاده میکند که در اسناد حقوقی، پزشکی و فنی پیچیده دقیقتر است.",
"pricing.faq.q4": "آیا اسناد من پس از ترجمه نگهداری میشوند؟",
"pricing.faq.a4": "فایلهای ترجمهشده طبق طرح شما در دسترس هستند (۳۰ روز مبتدی، ۹۰ روز حرفهای، ۱ سال سازمانی). آنها در حالت استراحت و هنگام انتقال رمزنگاری میشوند.",
"pricing.faq.q5": "اگر از سهمیه ماهانه خود فراتر رویم چه میشود؟",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "خطا در ایجاد پرداخت.",
"pricing.dashboard": "داشبورد",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "از ۰٫۰۹ دلار به ازای هر میلیون توکن",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "جایگزینها: DeepSeek V4 Pro و GLM-5.3",
+ "pricing.confirm.title": "اشتراک خود را تأیید کنید",
+ "pricing.confirm.subtitle": "به درگاه پرداخت امن ما هدایت میشوید.",
+ "pricing.confirm.cancel": "لغو",
+ "pricing.confirm.cta": "ادامه به پرداخت",
+ "pricing.confirm.year": "سال",
+ "pricing.confirm.month": "ماه",
+ "pricing.confirm.monthlyEquivalent": "صورتحساب سالانه — یعنی {price} یورو در ماه.",
+ "pricing.confirm.secureNote": "هر زمان از پروفایل خود قابل لغو است. پرداخت توسط Stripe انجام میشود؛ اطلاعات کارت شما هرگز به سرورهای ما نمیرسد.",
+ "pricing.enterprise.subject": "پیشنهاد سازمانی",
+ "pricing.enterpriseBand.cta": "تماس با ما",
+ "pricing.enterpriseBand.text": "حجم بالا، موتورهای اختصاصی، گزینههای درونسازمانی — صحبت کنیم.",
+ "pricing.error.server": "خطای سرور {status}",
+ "pricing.freeBand.cta": "رایگان شروع کنید",
+ "pricing.freeBand.text": "فقط میخواهید امتحان کنید؟ رایگان شروع کنید — ماهی ۵ سند، بدون کارت.",
+ "pricing.header.titleBase": "برنامهای برای",
+ "pricing.header.titleAccent": "هر نیاز",
+ "pricing.toast.close": "بستن",
+ "pricing.aiModels.premium.price": "۲ دلار / ۱۰ دلار به ازای هر میلیون توکن"
}
diff --git a/frontend/src/lib/i18n/messages/fa/providerTheme.json b/frontend/src/lib/i18n/messages/fa/providerTheme.json
index ae60371..7be5a48 100644
--- a/frontend/src/lib/i18n/messages/fa/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/fa/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "دسترسی یکپارچه به بهترین مدلهای متنباز بهینهسازیشده برای ترجمه.",
"providerTheme.openrouter_premium.badge": "فوقالعاده",
"providerTheme.openrouter_premium.subBadge": "حداکثر زمینه",
- "providerTheme.openrouter_premium.desc": "با کمک مدلهای پیشرفته (GPT-4o، Claude Sonnet 4.6) برای اسناد طولانی.",
+ "providerTheme.openrouter_premium.desc": "با کمک مدلهای پیشرفته (GPT-4o، Claude 5) برای اسناد طولانی.",
"providerTheme.zai.badge": "تخصصی",
"providerTheme.zai.subBadge": "مالی و حقوقی",
"providerTheme.zai.desc": "مدل تنظیمشده برای اصطلاحات تجاری سختگیرانه (حقوقی، مالی).",
diff --git a/frontend/src/lib/i18n/messages/fr/admin.json b/frontend/src/lib/i18n/messages/fr/admin.json
index 0d3f492..066cac1 100644
--- a/frontend/src/lib/i18n/messages/fr/admin.json
+++ b/frontend/src/lib/i18n/messages/fr/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Fournisseurs",
"admin.nav.system": "Système",
"admin.nav.logs": "Logs",
+ "admin.nav.stats": "Statistiques",
"admin.users.title": "Gestion des Utilisateurs",
"admin.users.subtitle": "Visualiser et gérer les comptes utilisateurs",
"admin.users.planUpdated": "Plan mis à jour",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "En attente de données...",
"admin.system.purging": "Purge...",
"admin.system.clean": "Nettoyer",
- "admin.system.purge": "Purger"
+ "admin.system.purge": "Purger",
+ "admin.nav.models": "Modèles & abonnements",
+ "admin.nav.marketing": "Marketing",
+ "admin.marketing.audience": "Audience",
+ "admin.marketing.audienceAllUsers": "Tous les comptes",
+ "admin.marketing.audienceInactive": "Comptes inactifs depuis 30 jours",
+ "admin.marketing.audiencePlaceholder": "Choisir une audience",
+ "admin.marketing.audiencePlanBusiness": "Forfait Business",
+ "admin.marketing.audiencePlanEnterprise": "Forfait Enterprise",
+ "admin.marketing.audiencePlanFree": "Forfait Gratuit",
+ "admin.marketing.audiencePlanPro": "Forfait Pro",
+ "admin.marketing.audiencePlanStarter": "Forfait Starter",
+ "admin.marketing.audienceWaitlist": "Liste d'attente",
+ "admin.marketing.badgeReal": "réel",
+ "admin.marketing.badgeTest": "test",
+ "admin.marketing.confirmSend": "Envoyer cet email à {count} destinataire(s) (« {audience} ») ?",
+ "admin.marketing.footerNote": "Un pied de page avec le lien de désabonnement est ajouté automatiquement à l'envoi.",
+ "admin.marketing.hidePreview": "Masquer l'aperçu",
+ "admin.marketing.history": "Historique des envois",
+ "admin.marketing.historyFailedSuffix": ", {count} échec(s)",
+ "admin.marketing.historySent": "{sent}/{total} envoyé(s)",
+ "admin.marketing.html": "Contenu HTML",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Renseignez le sujet et le HTML.",
+ "admin.marketing.incompleteTitle": "Contenu incomplet",
+ "admin.marketing.loadErrorTitle": "Erreur de chargement",
+ "admin.marketing.localCheckUnavailable": "Vérification locale indisponible sur ce navigateur : le serveur contrôlera l'envoi test préalable.",
+ "admin.marketing.networkDesc": "Impossible de contacter le backend.",
+ "admin.marketing.newCampaign": "Nouvelle relance",
+ "admin.marketing.noHistory": "Aucun envoi enregistré.",
+ "admin.marketing.preview": "Aperçu",
+ "admin.marketing.previewEmpty": "Aperçu : collez votre HTML ci-contre.",
+ "admin.marketing.previewTitle": "Aperçu de l'email",
+ "admin.marketing.recipientsAvailable": "{count} destinataire(s) disponibles après exclusion des désabonnés.",
+ "admin.marketing.sendReal": "Envoyer à {count} destinataire(s)",
+ "admin.marketing.sendRefusedTitle": "Envoi refusé",
+ "admin.marketing.sendTest": "Envoi test",
+ "admin.marketing.sendingDesc": "{count} email(s) en file d'envoi (0,2 s d'intervalle). Les désabonnés sont exclus.",
+ "admin.marketing.sendingTitle": "Envoi en cours",
+ "admin.marketing.subject": "Sujet",
+ "admin.marketing.subjectPlaceholder": "Ex. : Votre traduction vous attend — 20 % pendant 7 jours",
+ "admin.marketing.subtitle": "Envoi d'emails à une audience, avec envoi test obligatoire, lien de désabonnement automatique et historique complet.",
+ "admin.marketing.testDoneDesc": "Email reçu sur {email}",
+ "admin.marketing.testDoneTitle": "Envoi test effectué",
+ "admin.marketing.testEmail": "Email de test (optionnel)",
+ "admin.marketing.testEmailPlaceholder": "Sinon : adresse d'expédition SMTP",
+ "admin.marketing.testFailedTitle": "Échec de l'envoi test",
+ "admin.marketing.testRequiredHint": "L'envoi réel exige un envoi test préalable de ce contenu exact (vérifié par le serveur).",
+ "admin.marketing.testValidated": "Envoi test validé pour ce contenu.",
+ "admin.marketing.title": "Marketing — relances par email",
+ "admin.marketing.unsubscribedNote": "{count} désabonné(s) seront exclus de tout envoi.",
+ "admin.models.addModel": "Ajouter un modèle",
+ "admin.models.businessBadge": "Forfait Business",
+ "admin.models.cancel": "Annuler",
+ "admin.models.catalog": "Catalogue OpenRouter",
+ "admin.models.catalogErrorDesc": "Impossible de charger le catalogue OpenRouter.",
+ "admin.models.catalogErrorTitle": "Catalogue indisponible",
+ "admin.models.customBadge": "Palier personnalisé",
+ "admin.models.customNote": "Modèles sur mesure, définis au cas par cas avec le client.",
+ "admin.models.defaultBadge": "par défaut",
+ "admin.models.emptyTier": "Aucun modèle : la gamme officielle du plan sera utilisée.",
+ "admin.models.enterpriseBadge": "Forfait Enterprise",
+ "admin.models.essentialBadge": "Palier IA Essentielle",
+ "admin.models.essentialCost": "— facturation coût 1",
+ "admin.models.fallbackFirst": "(secours n°1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — vérifiez votre token admin.",
+ "admin.models.loadErrorTitle": "Erreur de chargement",
+ "admin.models.matrixDesc": "Modèles actifs par palier — l'ordre de la liste est la priorité de secours. Le bouton radio choisit le modèle par défaut, utilisé immédiatement sans redéploiement.",
+ "admin.models.matrixTitle": "Matrice des modèles par forfait",
+ "admin.models.missingConfig": "Configuration des paliers IA introuvable. Rechargez la page.",
+ "admin.models.moveDown": "Descendre",
+ "admin.models.moveUp": "Monter",
+ "admin.models.networkDesc": "Impossible de contacter le backend.",
+ "admin.models.networkTitle": "Erreur réseau",
+ "admin.models.premiumBadge": "Palier IA Premium",
+ "admin.models.premiumCost": "— facturation coût 5",
+ "admin.models.premiumReservedNote": "Réservé aux forfaits Business et Enterprise (moteur « openrouter_premium »).",
+ "admin.models.proBadge": "Forfait Pro",
+ "admin.models.remove": "Retirer",
+ "admin.models.save": "Enregistrer",
+ "admin.models.saveErrorTitle": "Erreur de sauvegarde",
+ "admin.models.saveNetworkDesc": "Impossible d'enregistrer la configuration.",
+ "admin.models.savedDesc": "Le nouveau modèle par défaut est utilisé dès la prochaine traduction, sans redéploiement.",
+ "admin.models.savedTitle": "Modèles enregistrés",
+ "admin.models.saving": "Enregistrement...",
+ "admin.models.setDefault": "Définir {model} comme modèle par défaut",
+ "admin.models.sharedTierNote": "Palier commun : le forfait Business utilise aussi la Essentielle pour son moteur « openrouter » — la liste ci-dessus s'applique aux deux forfaits.",
+ "admin.models.subtitle": "Matrice forfaits → paliers IA. Le modèle réellement utilisé par traduction est celui du palier du plan : un forfait Pro ne peut jamais déclencher un modèle Premium.",
+ "admin.models.title": "Modèles & abonnements",
+ "admin.stats.aiTiers": "Paliers IA (30 j)",
+ "admin.stats.includingCredits": "dont {amount} de crédits",
+ "admin.stats.mrr": "MRR estimé",
+ "admin.stats.noTranslationsYet": "Aucune traduction en base sur les 30 derniers jours : la répartition des paliers se remplira dès les prochaines traductions.",
+ "admin.stats.payments30": "{count} paiement(s) sur 30 jours",
+ "admin.stats.refreshing": "Actualisation...",
+ "admin.stats.revenue30": "Revenus (30 jours)",
+ "admin.stats.revenueTotal": "Revenus encaissés (total)",
+ "admin.stats.tiersSub": "Essentielle / Premium · classique {classic} · autre {other}",
+ "admin.stats.unavailable": "Statistiques business indisponibles ({error}).",
+ "admin.stats.waitlist": "Liste d'attente",
+ "admin.stats.waitlistSub": "inscrits en attente"
}
diff --git a/frontend/src/lib/i18n/messages/fr/landing.json b/frontend/src/lib/i18n/messages/fr/landing.json
index 5836fbc..308f7af 100644
--- a/frontend/src/lib/i18n/messages/fr/landing.json
+++ b/frontend/src/lib/i18n/messages/fr/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "Pour les professionnels exigeants",
"landing.pricing.pro.f1": "200 documents / mois",
"landing.pricing.pro.f2": "Jusqu'à 200 pages par doc",
- "landing.pricing.pro.f3": "Traduction par IA",
+ "landing.pricing.pro.f3": "IA Essentielle : DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google inclus",
"landing.pricing.pro.f5": "Glossaires et prompts",
"landing.pricing.pro.f6": "Support prioritaire",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "Pour les équipes avec des besoins élevés",
"landing.pricing.business.f1": "1 000 documents / mois",
"landing.pricing.business.f2": "Jusqu'à 500 pages par doc",
- "landing.pricing.business.f3": "IA Premium (Claude)",
+ "landing.pricing.business.f3": "IA Premium (Claude 5)",
"landing.pricing.business.f4": "Tous les fournisseurs + API",
"landing.pricing.business.f5": "Webhooks et automatisation",
"landing.pricing.business.f6": "5 postes d'équipe",
diff --git a/frontend/src/lib/i18n/messages/fr/pricing.json b/frontend/src/lib/i18n/messages/fr/pricing.json
index e24bba5..f261a41 100644
--- a/frontend/src/lib/i18n/messages/fr/pricing.json
+++ b/frontend/src/lib/i18n/messages/fr/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "Historique 90 jours",
"pricing.plans.business.feat1": "1 000 documents / mois",
"pricing.plans.business.feat2": "Jusqu'à 500 pages par document",
- "pricing.plans.business.feat3": "IA Essentielle + Premium (Claude Haiku)",
+ "pricing.plans.business.feat3": "IA Essentielle + IA Premium (Claude 5)",
"pricing.plans.business.feat4": "Tous les fournisseurs de traduction",
"pricing.plans.business.feat5": "Fichiers jusqu'à 50 Mo",
"pricing.plans.business.feat6": "Accès API (10 000 appels/mois)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "Historique 1 an",
"pricing.plans.business.feat10": "Analytiques avancées",
"pricing.plans.enterprise.feat1": "Documents illimités",
- "pricing.plans.enterprise.feat2": "Tous les modèles IA (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "Tous les modèles IA (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "Déploiement on-premise ou cloud dédié",
"pricing.plans.enterprise.feat4": "SLA 99,9 % garanti",
"pricing.plans.enterprise.feat5": "Support 24/7 dédié",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "IA multi-thread ultra-rapide",
"pricing.trust.availability.title": "Disponible 24/7",
"pricing.trust.availability.sub": "Uptime garanti 99,9 %",
- "pricing.aiModels.title": "Nos modèles IA — Mars 2026",
+ "pricing.aiModels.title": "Nos modèles IA — Septembre 2026",
"pricing.aiModels.essential.title": "Traduction IA Essentielle",
"pricing.aiModels.essential.plan": "Forfait Pro",
"pricing.aiModels.essential.descPrefix": "Basée sur",
- "pricing.aiModels.essential.descSuffix": "— le modèle IA le plus rentable de 2026. Qualité comparable aux modèles frontier à une fraction du coût.",
- "pricing.aiModels.essential.modelName": "notre modèle IA Essentiel",
- "pricing.aiModels.essential.context": "163K tokens de contexte",
- "pricing.aiModels.essential.value": "Excellent rapport qualité/prix",
+ "pricing.aiModels.essential.descSuffix": "— les modèles IA au meilleur rapport qualité/prix de 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash et MiniMax M3",
+ "pricing.aiModels.essential.context": "Jusqu'à 1,3 M de contexte (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "Le meilleur rapport qualité/prix",
"pricing.aiModels.premium.title": "Traduction IA Premium",
"pricing.aiModels.premium.plan": "Forfait Business",
"pricing.aiModels.premium.descPrefix": "Basée sur",
"pricing.aiModels.premium.descSuffix": "d'Anthropic — précis sur les documents juridiques, médicaux et techniques complexes.",
- "pricing.aiModels.premium.context": "200K tokens de contexte",
+ "pricing.aiModels.premium.context": "1M de contexte",
"pricing.aiModels.premium.precision": "Meilleure précision",
"pricing.faq.title": "Questions fréquentes",
"pricing.faq.q1": "Puis-je changer de forfait à tout moment ?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "Qu'est-ce que la « Traduction IA Essentielle » ?",
"pricing.faq.a2": "C'est notre moteur IA. Il comprend le contexte de vos documents, préserve la mise en page et gère les termes techniques bien mieux qu'une traduction classique.",
"pricing.faq.q3": "Quelle est la différence entre IA Essentielle et IA Premium ?",
- "pricing.faq.a3": "L'IA Essentielle utilise un modèle optimisé (excellent rapport qualité/prix). L'IA Premium utilise Claude Sonnet 4.6 d'Anthropic, plus précis sur les documents juridiques, médicaux et techniques complexes.",
+ "pricing.faq.a3": "L'IA Essentielle s'appuie sur DeepSeek V4 Flash, GLM-5.3 Flash et MiniMax M3 (excellent rapport qualité/prix). L'IA Premium utilise Claude 5 d'Anthropic, plus précis sur les documents juridiques, médicaux et techniques complexes.",
"pricing.faq.q4": "Mes documents sont-ils conservés après traduction ?",
"pricing.faq.a4": "Les fichiers traduits sont disponibles selon votre forfait (30 jours Starter, 90 jours Pro, 1 an Business). Ils sont chiffrés au repos et en transit.",
"pricing.faq.q5": "Que se passe-t-il si je dépasse mon quota mensuel ?",
@@ -163,5 +163,9 @@
"pricing.freeBand.text": "Juste envie d'essayer ? Commencez gratuitement — 5 documents par mois, sans carte.",
"pricing.freeBand.cta": "Commencer gratuitement",
"pricing.enterpriseBand.text": "Volume, moteurs dédiés, hébergement spécifique — parlons-en.",
- "pricing.enterpriseBand.cta": "Nous contacter"
+ "pricing.enterpriseBand.cta": "Nous contacter",
+ "pricing.aiModels.essential.price": "À partir de 0,09 $ par million de jetons",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Alternatives : DeepSeek V4 Pro et GLM-5.3",
+ "pricing.aiModels.premium.price": "2 $ / 10 $ par 1M de jetons"
}
diff --git a/frontend/src/lib/i18n/messages/fr/providerTheme.json b/frontend/src/lib/i18n/messages/fr/providerTheme.json
index e0b66e4..8db9a0c 100644
--- a/frontend/src/lib/i18n/messages/fr/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/fr/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Accès unifié aux meilleurs modèles open-source optimisés pour la traduction.",
"providerTheme.openrouter_premium.badge": "Ultra",
"providerTheme.openrouter_premium.subBadge": "Maximum Context",
- "providerTheme.openrouter_premium.desc": "Traduction assistée par les modèles de pointe (GPT-4o, Claude Sonnet 4.6) pour documents longs.",
+ "providerTheme.openrouter_premium.desc": "Traduction assistée par les modèles de pointe (GPT-4o, Claude 5) pour documents longs.",
"providerTheme.zai.badge": "Spécialisée",
"providerTheme.zai.subBadge": "Finance & Droit",
"providerTheme.zai.desc": "Modèle affiné pour les terminologies métiers exigeantes (juridique, finance).",
diff --git a/frontend/src/lib/i18n/messages/it/admin.json b/frontend/src/lib/i18n/messages/it/admin.json
index 9c805a4..4605c2f 100644
--- a/frontend/src/lib/i18n/messages/it/admin.json
+++ b/frontend/src/lib/i18n/messages/it/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Fornitori",
"admin.nav.system": "Sistema",
"admin.nav.logs": "Log",
+ "admin.nav.stats": "Statistiche",
"admin.users.title": "Gestione Utenti",
"admin.users.subtitle": "Visualizza e gestisci gli account utente",
"admin.users.planUpdated": "Piano aggiornato",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "In attesa di dati...",
"admin.system.purging": "Pulizia in corso...",
"admin.system.clean": "Pulisci",
- "admin.system.purge": "Pulisci"
+ "admin.system.purge": "Pulisci",
+ "admin.nav.models": "Modelli e abbonamenti",
+ "admin.nav.marketing": "Marketing",
+ "admin.marketing.audience": "Pubblico",
+ "admin.marketing.audienceAllUsers": "Tutti gli account",
+ "admin.marketing.audienceInactive": "Account inattivi da 30 giorni",
+ "admin.marketing.audiencePlaceholder": "Scegli un pubblico",
+ "admin.marketing.audiencePlanBusiness": "Piano Business",
+ "admin.marketing.audiencePlanEnterprise": "Piano Enterprise",
+ "admin.marketing.audiencePlanFree": "Piano Gratuito",
+ "admin.marketing.audiencePlanPro": "Piano Pro",
+ "admin.marketing.audiencePlanStarter": "Piano Starter",
+ "admin.marketing.audienceWaitlist": "Lista d'attesa",
+ "admin.marketing.badgeReal": "reale",
+ "admin.marketing.badgeTest": "prova",
+ "admin.marketing.confirmSend": "Inviare questa email a {count} destinatario/i («{audience}»)?",
+ "admin.marketing.footerNote": "Un piè di pagina con il link di disiscrizione è aggiunto automaticamente all'invio.",
+ "admin.marketing.hidePreview": "Nascondi anteprima",
+ "admin.marketing.history": "Cronologia degli invii",
+ "admin.marketing.historyFailedSuffix": ", {count} falliti",
+ "admin.marketing.historySent": "{sent}/{total} inviati",
+ "admin.marketing.html": "Contenuto HTML",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Compila l'oggetto e l'HTML.",
+ "admin.marketing.incompleteTitle": "Contenuto incompleto",
+ "admin.marketing.loadErrorTitle": "Errore di caricamento",
+ "admin.marketing.localCheckUnavailable": "Verifica locale non disponibile in questo browser: il server controllerà l'invio di prova previo.",
+ "admin.marketing.networkDesc": "Impossibile contattare il backend.",
+ "admin.marketing.newCampaign": "Nuovo invio",
+ "admin.marketing.noHistory": "Nessun invio registrato.",
+ "admin.marketing.preview": "Anteprima",
+ "admin.marketing.previewEmpty": "Anteprima: incolla qui il tuo HTML.",
+ "admin.marketing.previewTitle": "Anteprima dell'email",
+ "admin.marketing.recipientsAvailable": "{count} destinatario/i disponibili dopo l'esclusione dei disiscritti.",
+ "admin.marketing.sendReal": "Invia a {count} destinatario/i",
+ "admin.marketing.sendRefusedTitle": "Invio rifiutato",
+ "admin.marketing.sendTest": "Invio di prova",
+ "admin.marketing.sendingDesc": "{count} email in coda di invio (intervallo 0,2 s). I disiscritti sono esclusi.",
+ "admin.marketing.sendingTitle": "Invio in corso",
+ "admin.marketing.subject": "Oggetto",
+ "admin.marketing.subjectPlaceholder": "Es.: La tua traduzione ti aspetta — 20% per 7 giorni",
+ "admin.marketing.subtitle": "Invio di email a un pubblico, con invio di prova obbligatorio, link di disiscrizione automatico e cronologia completa.",
+ "admin.marketing.testDoneDesc": "Email ricevuta su {email}",
+ "admin.marketing.testDoneTitle": "Invio di prova effettuato",
+ "admin.marketing.testEmail": "Email di prova (opzionale)",
+ "admin.marketing.testEmailPlaceholder": "Altrimenti: indirizzo mittente SMTP",
+ "admin.marketing.testFailedTitle": "Fallimento dell'invio di prova",
+ "admin.marketing.testRequiredHint": "L'invio reale richiede un invio di prova previo di questo identico contenuto (verificato dal server).",
+ "admin.marketing.testValidated": "Invio di prova convalidato per questo contenuto.",
+ "admin.marketing.title": "Marketing — invii email",
+ "admin.marketing.unsubscribedNote": "{count} contatti disiscritti saranno esclusi da ogni invio.",
+ "admin.models.addModel": "Aggiungi un modello",
+ "admin.models.businessBadge": "Piano Business",
+ "admin.models.cancel": "Annulla",
+ "admin.models.catalog": "Catalogo OpenRouter",
+ "admin.models.catalogErrorDesc": "Impossibile caricare il catalogo OpenRouter.",
+ "admin.models.catalogErrorTitle": "Catalogo non disponibile",
+ "admin.models.customBadge": "Livello personalizzato",
+ "admin.models.customNote": "Modelli su misura, definiti caso per caso con il cliente.",
+ "admin.models.defaultBadge": "predefinito",
+ "admin.models.emptyTier": "Nessun modello: verrà usata la gamma ufficiale del piano.",
+ "admin.models.enterpriseBadge": "Piano Enterprise",
+ "admin.models.essentialBadge": "Livello IA Essenziale",
+ "admin.models.essentialCost": "— fatturato con fattore di costo 1",
+ "admin.models.fallbackFirst": "(fallback n. 1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — verifica il token amministratore.",
+ "admin.models.loadErrorTitle": "Errore di caricamento",
+ "admin.models.matrixDesc": "Modelli attivi per livello — l'ordine della lista è la priorità di fallback. Il pulsante di opzione sceglie il modello predefinito, attivo subito senza ridistribuzione.",
+ "admin.models.matrixTitle": "Matrice dei modelli per piano",
+ "admin.models.missingConfig": "Configurazione dei livelli IA non trovata. Ricarica la pagina.",
+ "admin.models.moveDown": "Sposta giù",
+ "admin.models.moveUp": "Sposta su",
+ "admin.models.networkDesc": "Impossibile contattare il backend.",
+ "admin.models.networkTitle": "Errore di rete",
+ "admin.models.premiumBadge": "Livello IA Premium",
+ "admin.models.premiumCost": "— fatturato con fattore di costo 5",
+ "admin.models.premiumReservedNote": "Riservato ai piani Business ed Enterprise (motore «openrouter_premium»).",
+ "admin.models.proBadge": "Piano Pro",
+ "admin.models.remove": "Rimuovi",
+ "admin.models.save": "Salva",
+ "admin.models.saveErrorTitle": "Errore di salvataggio",
+ "admin.models.saveNetworkDesc": "Impossibile salvare la configurazione.",
+ "admin.models.savedDesc": "Il nuovo modello predefinito è usato dalla prossima traduzione, senza ridistribuzione.",
+ "admin.models.savedTitle": "Modelli salvati",
+ "admin.models.saving": "Salvataggio...",
+ "admin.models.setDefault": "Imposta {model} come modello predefinito",
+ "admin.models.sharedTierNote": "Livello condiviso: il piano Business usa il livello Essenziale anche per il motore «openrouter» — l'elenco sopra vale per entrambi i piani.",
+ "admin.models.subtitle": "Matrice piani → livelli IA. Il modello realmente usato per ogni traduzione è quello del livello del piano: un piano Pro non può mai attivare un modello Premium.",
+ "admin.models.title": "Modelli e abbonamenti",
+ "admin.stats.aiTiers": "Livelli IA (30 g)",
+ "admin.stats.includingCredits": "di cui {amount} di crediti",
+ "admin.stats.mrr": "MRR stimato",
+ "admin.stats.noTranslationsYet": "Nessuna traduzione registrata negli ultimi 30 giorni: la ripartizione per livelli si completerà con le prossime traduzioni.",
+ "admin.stats.payments30": "{count} pagamento/i in 30 giorni",
+ "admin.stats.refreshing": "Aggiornamento...",
+ "admin.stats.revenue30": "Ricavi (30 giorni)",
+ "admin.stats.revenueTotal": "Ricavi incassati (totale)",
+ "admin.stats.tiersSub": "Essenziale / Premium · classico {classic} · altro {other}",
+ "admin.stats.unavailable": "Statistiche business non disponibili ({error}).",
+ "admin.stats.waitlist": "Lista d'attesa",
+ "admin.stats.waitlistSub": "iscritti in attesa"
}
diff --git a/frontend/src/lib/i18n/messages/it/landing.json b/frontend/src/lib/i18n/messages/it/landing.json
index 0f27f50..b951cb2 100644
--- a/frontend/src/lib/i18n/messages/it/landing.json
+++ b/frontend/src/lib/i18n/messages/it/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "Per professionisti esigenti",
"landing.pricing.pro.f1": "200 documenti / mese",
"landing.pricing.pro.f2": "Fino a 200 pagine per documento",
- "landing.pricing.pro.f3": "Traduzione con IA",
+ "landing.pricing.pro.f3": "IA Essenziale: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google inclusi",
"landing.pricing.pro.f5": "Glossari e prompt personalizzati",
"landing.pricing.pro.f6": "Supporto prioritario",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "Per team con esigenze elevate",
"landing.pricing.business.f1": "1 000 documenti / mese",
"landing.pricing.business.f2": "Fino a 500 pagine per documento",
- "landing.pricing.business.f3": "IA Premium (Claude)",
+ "landing.pricing.business.f3": "IA Premium (Claude 5)",
"landing.pricing.business.f4": "Tutti i provider + accesso API",
"landing.pricing.business.f5": "Webhook e automazione",
"landing.pricing.business.f6": "5 postazioni team",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "File DOCX, XLSX, PPTX o PDF supportati",
"landing.translate.aiAnalysis": "Analisi IA Attiva",
"landing.translate.processing": "Elaborazione in corso",
- "landing.translate.preservingLayout": "Il tuo layout viene preservato"
+ "landing.translate.preservingLayout": "Il tuo layout viene preservato",
+ "landing.beforeAfter.seal": "Stesso layout, parola per parola",
+ "landing.beforeAfter.proof1": "I diagrammi SmartArt vengono ricostruiti, non appiattiti",
+ "landing.beforeAfter.proof2": "Serie e assi dei grafici tradotti",
+ "landing.beforeAfter.proof3": "Indici rigenerati nella lingua di destinazione",
+ "landing.beforeAfter.targetTitle": "Specifiche tecniche — Trattamento aria",
+ "landing.beforeAfter.targetPara1": "L'impianto di ventilazione deve essere messo in servizio entro la fine del secondo trimestre.",
+ "landing.beforeAfter.targetPara2Before": "",
+ "landing.beforeAfter.targetTerm": "L'unità di trattamento aria",
+ "landing.beforeAfter.targetPara2After": "deve rispettare la classe di filtraggio F7 come da programma.",
+ "landing.beforeAfter.targetItem1": "Carico termico: 42 kW alla portata nominale",
+ "landing.beforeAfter.targetItem2": "Livello sonoro inferiore a 45 dB(A) a 3 m",
+ "landing.formats.pill": "COMPATIBILITÀ",
+ "landing.hero.visualCaption": "Stesso layout, nuova lingua — il resto non si tocca",
+ "landing.pricing.free.name": "Gratuito",
+ "landing.pricing.free.desc": "Ideale per scoprire l'app",
+ "landing.pricing.free.cta": "Scegli questo piano",
+ "landing.pricing.enterprise.name": "Enterprise",
+ "landing.pricing.enterprise.desc": "Soluzioni su misura per grandi organizzazioni",
+ "landing.pricing.enterprise.cta": "Contattaci",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/it/pricing.json b/frontend/src/lib/i18n/messages/it/pricing.json
index 58157b8..2ff32bf 100644
--- a/frontend/src/lib/i18n/messages/it/pricing.json
+++ b/frontend/src/lib/i18n/messages/it/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "Cronologia di 90 giorni",
"pricing.plans.business.feat1": "1 000 documenti / mese",
"pricing.plans.business.feat2": "Fino a 500 pagine per documento",
- "pricing.plans.business.feat3": "IA Essenziale + Premium (Claude Haiku)",
+ "pricing.plans.business.feat3": "IA Essenziale + IA Premium (Claude 5)",
"pricing.plans.business.feat4": "Tutti i provider di traduzione",
"pricing.plans.business.feat5": "File fino a 50 MB",
"pricing.plans.business.feat6": "Accesso API (10 000 chiamate/mese)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "Cronologia di 1 anno",
"pricing.plans.business.feat10": "Analisi avanzate",
"pricing.plans.enterprise.feat1": "Documenti illimitati",
- "pricing.plans.enterprise.feat2": "Tutti i modelli IA (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "Tutti i modelli IA (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "Distribuzione on-premise o cloud dedicato",
"pricing.plans.enterprise.feat4": "SLA 99,9 % garantito",
"pricing.plans.enterprise.feat5": "Supporto dedicato 24/7",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "IA multi-thread ultraveloce",
"pricing.trust.availability.title": "Disponibile 24/7",
"pricing.trust.availability.sub": "99,9 % di uptime garantito",
- "pricing.aiModels.title": "I nostri modelli IA — Marzo 2026",
+ "pricing.aiModels.title": "I nostri modelli IA — Settembre 2026",
"pricing.aiModels.essential.title": "Traduzione IA Essenziale",
"pricing.aiModels.essential.plan": "Piano Pro",
"pricing.aiModels.essential.descPrefix": "Basato su",
- "pricing.aiModels.essential.descSuffix": "— il modello IA più conveniente del 2026. Qualità paragonabile ai modelli frontier a una frazione del costo.",
- "pricing.aiModels.essential.modelName": "il nostro modello IA Essenziale",
- "pricing.aiModels.essential.context": "163K token di contesto",
- "pricing.aiModels.essential.value": "Eccellente rapporto qualità-prezzo",
+ "pricing.aiModels.essential.descSuffix": "— i modelli IA con il miglior rapporto qualità-prezzo del 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash e MiniMax M3",
+ "pricing.aiModels.essential.context": "Fino a 1,3 M di contesto (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "Il miglior rapporto qualità-prezzo",
"pricing.aiModels.premium.title": "Traduzione IA Premium",
"pricing.aiModels.premium.plan": "Piano Business",
"pricing.aiModels.premium.descPrefix": "Basato su",
"pricing.aiModels.premium.descSuffix": "di Anthropic — preciso su documenti legali, medici e tecnici complessi.",
- "pricing.aiModels.premium.context": "200K token di contesto",
+ "pricing.aiModels.premium.context": "1M di contesto",
"pricing.aiModels.premium.precision": "Massima precisione",
"pricing.faq.title": "Domande frequenti",
"pricing.faq.q1": "Posso cambiare piano in qualsiasi momento?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "Cos'è la «Traduzione IA Essenziale»?",
"pricing.faq.a2": "È il nostro motore IA. Comprende il contesto dei vostri documenti, preserva il layout e gestisce i termini tecnici molto meglio della traduzione classica.",
"pricing.faq.q3": "Qual è la differenza tra IA Essenziale e IA Premium?",
- "pricing.faq.a3": "La IA Essenziale usa un modello ottimizzato (eccellente rapporto qualità/prezzo). La IA Premium usa Claude 3.5 Haiku di Anthropic, più precisa su documenti legali, medici e tecnici complessi.",
+ "pricing.faq.a3": "La IA Essenziale si basa su DeepSeek V4 Flash, GLM-5.3 Flash e MiniMax M3 (eccellente rapporto qualità/prezzo). La IA Premium usa Claude 5 di Anthropic, più precisa su documenti legali, medici e tecnici complessi.",
"pricing.faq.q4": "I miei documenti vengono conservati dopo la traduzione?",
"pricing.faq.a4": "I file tradotti sono disponibili secondo il tuo piano (30 giorni Starter, 90 giorni Pro, 1 anno Business). Sono crittografati a riposo e in transito.",
"pricing.faq.q5": "Cosa succede se supero la quota mensile?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "Errore durante la creazione del pagamento.",
"pricing.dashboard": "Dashboard",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "Da 0,09 $ per milione di token",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Alternative: DeepSeek V4 Pro e GLM-5.3",
+ "pricing.confirm.title": "Conferma il tuo abbonamento",
+ "pricing.confirm.subtitle": "Verrai reindirizzato al nostro fornitore di pagamento sicuro.",
+ "pricing.confirm.cancel": "Annulla",
+ "pricing.confirm.cta": "Vai al pagamento",
+ "pricing.confirm.year": "anno",
+ "pricing.confirm.month": "mese",
+ "pricing.confirm.monthlyEquivalent": "Fatturazione annuale — ovvero {price} € / mese.",
+ "pricing.confirm.secureNote": "Disdicibile in qualsiasi momento dal tuo profilo. Il pagamento è elaborato da Stripe; i dati della tua carta non passano mai dai nostri server.",
+ "pricing.enterprise.subject": "Richiesta Enterprise",
+ "pricing.enterpriseBand.cta": "Contattaci",
+ "pricing.enterpriseBand.text": "Volume, motori dedicati, opzioni on-premise — parliamone.",
+ "pricing.error.server": "Errore del server {status}",
+ "pricing.freeBand.cta": "Inizia gratis",
+ "pricing.freeBand.text": "Vuoi solo provare? Inizia gratis — 5 documenti al mese, senza carta.",
+ "pricing.header.titleBase": "Un piano per",
+ "pricing.header.titleAccent": "ogni esigenza",
+ "pricing.toast.close": "Chiudi",
+ "pricing.aiModels.premium.price": "2 $ / 10 $ per 1M di token"
}
diff --git a/frontend/src/lib/i18n/messages/it/providerTheme.json b/frontend/src/lib/i18n/messages/it/providerTheme.json
index 4b426f2..e4a8220 100644
--- a/frontend/src/lib/i18n/messages/it/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/it/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Accesso unificato ai migliori modelli open-source ottimizzati per la traduzione.",
"providerTheme.openrouter_premium.badge": "Ultra",
"providerTheme.openrouter_premium.subBadge": "Contesto massimo",
- "providerTheme.openrouter_premium.desc": "Assistito da modelli all'avanguardia (GPT-4o, Claude Sonnet 4.6) per documenti lunghi.",
+ "providerTheme.openrouter_premium.desc": "Assistito da modelli all'avanguardia (GPT-4o, Claude 5) per documenti lunghi.",
"providerTheme.zai.badge": "Specializzata",
"providerTheme.zai.subBadge": "Finanza e Diritto",
"providerTheme.zai.desc": "Modello ottimizzato per terminologie aziendali impegnative (legale, finanza).",
diff --git a/frontend/src/lib/i18n/messages/ja/admin.json b/frontend/src/lib/i18n/messages/ja/admin.json
index b177be8..ea84fa8 100644
--- a/frontend/src/lib/i18n/messages/ja/admin.json
+++ b/frontend/src/lib/i18n/messages/ja/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "プロバイダー",
"admin.nav.system": "システム",
"admin.nav.logs": "ログ",
+ "admin.nav.stats": "統計",
"admin.users.title": "ユーザー管理",
"admin.users.subtitle": "ユーザーアカウントの表示と管理",
"admin.users.planUpdated": "プランが更新されました",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "データを待機中...",
"admin.system.purging": "パージ中...",
"admin.system.clean": "クリーン",
- "admin.system.purge": "パージ"
+ "admin.system.purge": "パージ",
+ "admin.nav.models": "モデル & サブスクリプション",
+ "admin.nav.marketing": "マーケティング",
+ "admin.marketing.audience": "対象者",
+ "admin.marketing.audienceAllUsers": "全アカウント",
+ "admin.marketing.audienceInactive": "30日間非アクティブなアカウント",
+ "admin.marketing.audiencePlaceholder": "対象者を選択",
+ "admin.marketing.audiencePlanBusiness": "Businessプラン",
+ "admin.marketing.audiencePlanEnterprise": "Enterpriseプラン",
+ "admin.marketing.audiencePlanFree": "無料プラン",
+ "admin.marketing.audiencePlanPro": "Proプラン",
+ "admin.marketing.audiencePlanStarter": "Starterプラン",
+ "admin.marketing.audienceWaitlist": "ウェイトリスト",
+ "admin.marketing.badgeReal": "本番",
+ "admin.marketing.badgeTest": "テスト",
+ "admin.marketing.confirmSend": "このメールを {count} 名(「{audience}」)に送信しますか?",
+ "admin.marketing.footerNote": "送信時、配信停止リンク付きフッターが自動で追加されます。",
+ "admin.marketing.hidePreview": "プレビューを隠す",
+ "admin.marketing.history": "送信履歴",
+ "admin.marketing.historyFailedSuffix": "、{count} 件失敗",
+ "admin.marketing.historySent": "{sent}/{total} 送信済み",
+ "admin.marketing.html": "HTMLコンテンツ",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "件名とHTMLを入力してください。",
+ "admin.marketing.incompleteTitle": "内容が不完全です",
+ "admin.marketing.loadErrorTitle": "読み込みエラー",
+ "admin.marketing.localCheckUnavailable": "このブラウザではローカル確認ができないため、サーバーが事前テスト送信を検証します。",
+ "admin.marketing.networkDesc": "バックエンドに接続できません。",
+ "admin.marketing.newCampaign": "新しいキャンペーン",
+ "admin.marketing.noHistory": "送信履歴はありません。",
+ "admin.marketing.preview": "プレビュー",
+ "admin.marketing.previewEmpty": "プレビュー:左側にHTMLを貼り付けてください。",
+ "admin.marketing.previewTitle": "メールプレビュー",
+ "admin.marketing.recipientsAvailable": "配信停止を除いた {count} 名に送信できます。",
+ "admin.marketing.sendReal": "{count} 名に送信",
+ "admin.marketing.sendRefusedTitle": "送信が拒否されました",
+ "admin.marketing.sendTest": "テスト送信",
+ "admin.marketing.sendingDesc": "{count} 件が送信キューに入りました(0.2秒間隔)。配信停止中の宛先は除外されます。",
+ "admin.marketing.sendingTitle": "送信中",
+ "admin.marketing.subject": "件名",
+ "admin.marketing.subjectPlaceholder": "例:翻訳ができあがっています — 7日間20%オフ",
+ "admin.marketing.subtitle": "対象者へのメール送信。テスト送信が必須で、配信停止リンク自動付加、完全な履歴付き。",
+ "admin.marketing.testDoneDesc": "{email} でメールを受信",
+ "admin.marketing.testDoneTitle": "テスト送信が完了しました",
+ "admin.marketing.testEmail": "テストメール(任意)",
+ "admin.marketing.testEmailPlaceholder": "未指定の場合:SMTP送信者アドレス",
+ "admin.marketing.testFailedTitle": "テスト送信に失敗しました",
+ "admin.marketing.testRequiredHint": "本送信には、同一内容の事前テスト送信が必要です(サーバーが検証します)。",
+ "admin.marketing.testValidated": "この内容のテスト送信は検証済みです。",
+ "admin.marketing.title": "マーケティング — メールキャンペーン",
+ "admin.marketing.unsubscribedNote": "配信停止中の {count} 件はすべての送信から除外されます。",
+ "admin.models.addModel": "モデルを追加",
+ "admin.models.businessBadge": "Businessプラン",
+ "admin.models.cancel": "キャンセル",
+ "admin.models.catalog": "OpenRouterカタログ",
+ "admin.models.catalogErrorDesc": "OpenRouterカタログを読み込めませんでした。",
+ "admin.models.catalogErrorTitle": "カタログを利用できません",
+ "admin.models.customBadge": "カスタムティア",
+ "admin.models.customNote": "カスタムモデルはお客様ごとに個別に決定します。",
+ "admin.models.defaultBadge": "既定",
+ "admin.models.emptyTier": "モデルなし:プランの公式ラインナップが使用されます。",
+ "admin.models.enterpriseBadge": "Enterpriseプラン",
+ "admin.models.essentialBadge": "エッセンシャルAIティア",
+ "admin.models.essentialCost": "— コスト係数 1 で課金",
+ "admin.models.fallbackFirst": "(フォールバック第1位)",
+ "admin.models.loadErrorDesc": "HTTP {status} — 管理者トークンを確認してください。",
+ "admin.models.loadErrorTitle": "読み込みエラー",
+ "admin.models.matrixDesc": "ティアごとの有効モデル — リストの順序がフォールバック優先度です。ラジオボタンで既定モデルを選ぶと、再デプロイなしですぐに反映されます。",
+ "admin.models.matrixTitle": "プラン別モデルマトリクス",
+ "admin.models.missingConfig": "AIティアの設定が見つかりません。ページを再読み込みしてください。",
+ "admin.models.moveDown": "下へ移動",
+ "admin.models.moveUp": "上へ移動",
+ "admin.models.networkDesc": "バックエンドに接続できません。",
+ "admin.models.networkTitle": "ネットワークエラー",
+ "admin.models.premiumBadge": "プレミアムAIティア",
+ "admin.models.premiumCost": "— コスト係数 5 で課金",
+ "admin.models.premiumReservedNote": "Business・Enterpriseプラン専用(「openrouter_premium」エンジン)。",
+ "admin.models.proBadge": "Proプラン",
+ "admin.models.remove": "削除",
+ "admin.models.save": "保存",
+ "admin.models.saveErrorTitle": "保存エラー",
+ "admin.models.saveNetworkDesc": "設定を保存できませんでした。",
+ "admin.models.savedDesc": "新しい既定モデルは次の翻訳から使用されます(再デプロイ不要)。",
+ "admin.models.savedTitle": "モデルを保存しました",
+ "admin.models.saving": "保存中...",
+ "admin.models.setDefault": "{model} を既定モデルに設定",
+ "admin.models.sharedTierNote": "共通ティア:Businessプランも「openrouter」エンジンにはエッセンシャルティアを使います — 上のリストは両プランに適用されます。",
+ "admin.models.subtitle": "プラン → AIティアのマトリクス。翻訳ごとに実際に使われるモデルはプランのティアのモデルです:Proプランがプレミアムモデルを呼び出すことは決してありません。",
+ "admin.models.title": "モデル & サブスクリプション",
+ "admin.stats.aiTiers": "AIティア(30日)",
+ "admin.stats.includingCredits": "うちクレジット {amount}",
+ "admin.stats.mrr": "推定MRR",
+ "admin.stats.noTranslationsYet": "過去30日間の翻訳記録がありません:今後の翻訳でティア別内訳が埋まります。",
+ "admin.stats.payments30": "30日間の支払い {count} 件",
+ "admin.stats.refreshing": "更新中...",
+ "admin.stats.revenue30": "売上(30日)",
+ "admin.stats.revenueTotal": "累計売上",
+ "admin.stats.tiersSub": "エッセンシャル / プレミアム · クラシック {classic} · その他 {other}",
+ "admin.stats.unavailable": "ビジネス統計を利用できません({error})。",
+ "admin.stats.waitlist": "ウェイトリスト",
+ "admin.stats.waitlistSub": "待機中の登録者"
}
diff --git a/frontend/src/lib/i18n/messages/ja/landing.json b/frontend/src/lib/i18n/messages/ja/landing.json
index 272de55..07e63f9 100644
--- a/frontend/src/lib/i18n/messages/ja/landing.json
+++ b/frontend/src/lib/i18n/messages/ja/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "プロフェッショナル向け",
"landing.pricing.pro.f1": "月200ドキュメント",
"landing.pricing.pro.f2": "1ドキュメント最大200ページ",
- "landing.pricing.pro.f3": "AI翻訳",
+ "landing.pricing.pro.f3": "エッセンシャルAI:DeepSeek V4 Flash、GLM-5.3 Flash、MiniMax M3",
"landing.pricing.pro.f4": "Google込み",
"landing.pricing.pro.f5": "カスタム用語集とプロンプト",
"landing.pricing.pro.f6": "優先サポート",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "大量処理が必要なチーム向け",
"landing.pricing.business.f1": "月1,000ドキュメント",
"landing.pricing.business.f2": "1ドキュメント最大500ページ",
- "landing.pricing.business.f3": "プレミアムAI(Claude)",
+ "landing.pricing.business.f3": "プレミアムAI(Claude 5)",
"landing.pricing.business.f4": "全プロバイダー + API",
"landing.pricing.business.f5": "Webhookと自動化",
"landing.pricing.business.f6": "チームシート5席",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "DOCX, XLSX, PPTX, PDFファイル対応",
"landing.translate.aiAnalysis": "AI分析アクティブ",
"landing.translate.processing": "処理中",
- "landing.translate.preservingLayout": "レイアウトを保持しています"
+ "landing.translate.preservingLayout": "レイアウトを保持しています",
+ "landing.beforeAfter.seal": "同じレイアウト、一語一句そのまま",
+ "landing.beforeAfter.proof1": "SmartArt図面は平面化されず、再構築されます",
+ "landing.beforeAfter.proof2": "グラフの系列と軸も翻訳",
+ "landing.beforeAfter.proof3": "目次は翻訳先言語で再生成",
+ "landing.beforeAfter.targetTitle": "技術仕様 — 空調処理",
+ "landing.beforeAfter.targetPara1": "換気設備は第2四半期末までに稼働させなければなりません。",
+ "landing.beforeAfter.targetPara2Before": "",
+ "landing.beforeAfter.targetTerm": "空調機",
+ "landing.beforeAfter.targetPara2After": "は計画どおりフィルタクラス F7 を満たす必要があります。",
+ "landing.beforeAfter.targetItem1": "熱負荷:定格流量で42 kW",
+ "landing.beforeAfter.targetItem2": "騒音レベル:3 mで45 dB(A)未満",
+ "landing.formats.pill": "対応フォーマット",
+ "landing.hero.visualCaption": "同じレイアウト、新しい言語 — 他は一切変わりません",
+ "landing.pricing.free.name": "無料",
+ "landing.pricing.free.desc": "アプリのお試しに最適",
+ "landing.pricing.free.cta": "このプランを選択",
+ "landing.pricing.enterprise.name": "エンタープライズ",
+ "landing.pricing.enterprise.desc": "大規模組織向けのカスタムソリューション",
+ "landing.pricing.enterprise.cta": "お問い合わせ",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/ja/pricing.json b/frontend/src/lib/i18n/messages/ja/pricing.json
index be70d2d..aace9c5 100644
--- a/frontend/src/lib/i18n/messages/ja/pricing.json
+++ b/frontend/src/lib/i18n/messages/ja/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "90日間の履歴",
"pricing.plans.business.feat1": "月1,000ドキュメント",
"pricing.plans.business.feat2": "1ドキュメントあたり最大500ページ",
- "pricing.plans.business.feat3": "エッセンシャル + プレミアムAI(Claude Haiku)",
+ "pricing.plans.business.feat3": "エッセンシャル + プレミアムAI(Claude 5)",
"pricing.plans.business.feat4": "全翻訳プロバイダー",
"pricing.plans.business.feat5": "最大50 MBのファイル",
"pricing.plans.business.feat6": "APIアクセス(月10,000コール)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "1年間の履歴",
"pricing.plans.business.feat10": "高度な分析",
"pricing.plans.enterprise.feat1": "無制限ドキュメント",
- "pricing.plans.enterprise.feat2": "全AIモデル(GPT-5、Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "全AIモデル(Claude 5、DeepSeek V4 Pro、GLM-5.3…)",
"pricing.plans.enterprise.feat3": "オンプレミスまたは専用クラウド展開",
"pricing.plans.enterprise.feat4": "99.9% SLA保証",
"pricing.plans.enterprise.feat5": "24/7専任サポート",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "超高速マルチスレッドAI",
"pricing.trust.availability.title": "24/7利用可能",
"pricing.trust.availability.sub": "99.9%稼働率保証",
- "pricing.aiModels.title": "AIモデル一覧 — 2026年3月",
+ "pricing.aiModels.title": "AIモデル一覧 — 2026年9月",
"pricing.aiModels.essential.title": "エッセンシャルAI翻訳",
"pricing.aiModels.essential.plan": "Proプラン",
"pricing.aiModels.essential.descPrefix": "ベース:",
- "pricing.aiModels.essential.descSuffix": "— 2026年で最も費用対効果の高いAIモデル。フロンティアモデルと同等の品質をわずかなコストで実現。",
- "pricing.aiModels.essential.modelName": "Essential AIモデル",
- "pricing.aiModels.essential.context": "163Kトークンのコンテキスト",
- "pricing.aiModels.essential.value": "優れた費用対効果",
+ "pricing.aiModels.essential.descSuffix": "— 2026年で最も費用対効果の高いAIモデル群。",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash、GLM-5.3 Flash、MiniMax M3",
+ "pricing.aiModels.essential.context": "最大130万トークンのコンテキスト(GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "最高のコストパフォーマンス",
"pricing.aiModels.premium.title": "プレミアムAI翻訳",
"pricing.aiModels.premium.plan": "Businessプラン",
"pricing.aiModels.premium.descPrefix": "ベース:",
"pricing.aiModels.premium.descSuffix": "(Anthropic社製)— 法務、医療、複雑な技術文書に高精度。",
- "pricing.aiModels.premium.context": "200Kトークンのコンテキスト",
+ "pricing.aiModels.premium.context": "100万トークンのコンテキスト",
"pricing.aiModels.premium.precision": "最高精度",
"pricing.faq.title": "よくある質問",
"pricing.faq.q1": "いつでもプランを変更できますか?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "「エッセンシャルAI翻訳」とは何ですか?",
"pricing.faq.a2": "独自のAIエンジンです。ドキュメントの文脈を理解し、レイアウトを保持し、技術用語を従来の翻訳よりはるかに適切に処理します。",
"pricing.faq.q3": "エッセンシャルAIとプレミアムAIの違いは何ですか?",
- "pricing.faq.a3": "Essential AIは最適化されたモデルを使用しています(コストパフォーマンスに優れています)。Premium AIはAnthropicのClaude 3.5 Haikuを使用し、法的、医学的、複雑な技術文書により正確です。",
+ "pricing.faq.a3": "Essential AIはDeepSeek V4 Flash、GLM-5.3 Flash、MiniMax M3を使用しています(コストパフォーマンスに優れています)。Premium AIはAnthropicのClaude 5を使用し、法的、医学的、複雑な技術文書により正確です。",
"pricing.faq.q4": "翻訳後もドキュメントは保存されますか?",
"pricing.faq.a4": "翻訳済みファイルはプランに応じて利用可能です(Starter 30日、Pro 90日、Business 1年)。保存時および通信時は暗号化されています。",
"pricing.faq.q5": "月間枠を超えた場合はどうなりますか?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "支払いの作成中にエラーが発生しました。",
"pricing.dashboard": "ダッシュボード",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "100万トークンあたり$0.09から",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "代替モデル:DeepSeek V4 Pro、GLM-5.3",
+ "pricing.confirm.title": "サブスクリプションの確認",
+ "pricing.confirm.subtitle": "安全な決済プロバイダーへ移動します。",
+ "pricing.confirm.cancel": "キャンセル",
+ "pricing.confirm.cta": "支払いへ進む",
+ "pricing.confirm.year": "年",
+ "pricing.confirm.month": "月",
+ "pricing.confirm.monthlyEquivalent": "年間請求 — 月あたり {price} ユーロ相当。",
+ "pricing.confirm.secureNote": "プロフィールからいつでも解約できます。決済はStripeが処理し、カード情報が当社のサーバーに送信されることはありません。",
+ "pricing.enterprise.subject": "エンタープライズプランについてのお問い合わせ",
+ "pricing.enterpriseBand.cta": "お問い合わせ",
+ "pricing.enterpriseBand.text": "大量処理、専用エンジン、オンプレミス対応 — ご相談ください。",
+ "pricing.error.server": "サーバーエラー {status}",
+ "pricing.freeBand.cta": "無料で始める",
+ "pricing.freeBand.text": "まず試してみたい方へ — 毎月5件まで無料、カード不要。",
+ "pricing.header.titleBase": "あらゆるニーズに",
+ "pricing.header.titleAccent": "ぴったりのプラン",
+ "pricing.toast.close": "閉じる",
+ "pricing.aiModels.premium.price": "100万トークンあたり $2 / $10"
}
diff --git a/frontend/src/lib/i18n/messages/ja/providerTheme.json b/frontend/src/lib/i18n/messages/ja/providerTheme.json
index 9347074..b9add17 100644
--- a/frontend/src/lib/i18n/messages/ja/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/ja/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "翻訳に最適化された最高のオープンソースモデルへの統一アクセス。",
"providerTheme.openrouter_premium.badge": "ウルトラ",
"providerTheme.openrouter_premium.subBadge": "最大コンテキスト",
- "providerTheme.openrouter_premium.desc": "GPT-4o、Claude Sonnet 4.6 などの最先端モデルによる長文支援。",
+ "providerTheme.openrouter_premium.desc": "GPT-4o、Claude 5 などの最先端モデルによる長文支援。",
"providerTheme.zai.badge": "特化型",
"providerTheme.zai.subBadge": "金融と法律",
"providerTheme.zai.desc": "要求の厳しいビジネス用語 (法務、金融) 向けに微調整されたモデル。",
diff --git a/frontend/src/lib/i18n/messages/ko/admin.json b/frontend/src/lib/i18n/messages/ko/admin.json
index e88b24d..9c23a00 100644
--- a/frontend/src/lib/i18n/messages/ko/admin.json
+++ b/frontend/src/lib/i18n/messages/ko/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "제공업체",
"admin.nav.system": "시스템",
"admin.nav.logs": "로그",
+ "admin.nav.stats": "통계",
"admin.users.title": "사용자 관리",
"admin.users.subtitle": "사용자 계정 보기 및 관리",
"admin.users.planUpdated": "플랜이 업데이트되었습니다",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "데이터 대기 중...",
"admin.system.purging": "삭제 중...",
"admin.system.clean": "정리",
- "admin.system.purge": "삭제"
+ "admin.system.purge": "삭제",
+ "admin.nav.models": "모델 & 구독",
+ "admin.nav.marketing": "마케팅",
+ "admin.marketing.audience": "대상",
+ "admin.marketing.audienceAllUsers": "전체 계정",
+ "admin.marketing.audienceInactive": "30일간 비활성 계정",
+ "admin.marketing.audiencePlaceholder": "대상 선택",
+ "admin.marketing.audiencePlanBusiness": "Business 플랜",
+ "admin.marketing.audiencePlanEnterprise": "Enterprise 플랜",
+ "admin.marketing.audiencePlanFree": "무료 플랜",
+ "admin.marketing.audiencePlanPro": "Pro 플랜",
+ "admin.marketing.audiencePlanStarter": "Starter 플랜",
+ "admin.marketing.audienceWaitlist": "대기 명단",
+ "admin.marketing.badgeReal": "실제",
+ "admin.marketing.badgeTest": "테스트",
+ "admin.marketing.confirmSend": "이 메일을 {count}명(«{audience}»)에게 보내시겠습니까?",
+ "admin.marketing.footerNote": "발송 시 수신거부 링크가 있는 바닥글이 자동으로 추가됩니다.",
+ "admin.marketing.hidePreview": "미리보기 숨기기",
+ "admin.marketing.history": "발송 이력",
+ "admin.marketing.historyFailedSuffix": ", {count}건 실패",
+ "admin.marketing.historySent": "{sent}/{total} 발송",
+ "admin.marketing.html": "HTML 내용",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "제목과 HTML을 입력하세요.",
+ "admin.marketing.incompleteTitle": "내용 불완전",
+ "admin.marketing.loadErrorTitle": "불러오기 오류",
+ "admin.marketing.localCheckUnavailable": "이 브라우저에서는 로컬 확인이 불가하여 서버가 사전 테스트 발송을 검증합니다.",
+ "admin.marketing.networkDesc": "백엔드에 연결할 수 없습니다.",
+ "admin.marketing.newCampaign": "새 캠페인",
+ "admin.marketing.noHistory": "발송 기록이 없습니다.",
+ "admin.marketing.preview": "미리보기",
+ "admin.marketing.previewEmpty": "미리보기: 왼쪽에 HTML을 붙여넣으세요.",
+ "admin.marketing.previewTitle": "이메일 미리보기",
+ "admin.marketing.recipientsAvailable": "수신거부 제외 후 {count}명에게 발송할 수 있습니다.",
+ "admin.marketing.sendReal": "{count}명에게 보내기",
+ "admin.marketing.sendRefusedTitle": "발송이 거부되었습니다",
+ "admin.marketing.sendTest": "테스트 발송",
+ "admin.marketing.sendingDesc": "{count}건이 발송 대기열에 있습니다(0.2초 간격). 수신거부는 제외됩니다.",
+ "admin.marketing.sendingTitle": "발송 진행 중",
+ "admin.marketing.subject": "제목",
+ "admin.marketing.subjectPlaceholder": "예: 번역이 기다리고 있습니다 — 7일간 20% 할인",
+ "admin.marketing.subtitle": "대상에게 이메일을 보냅니다. 필수 테스트 발송, 자동 수신거부 링크, 전체 이력이 포함됩니다.",
+ "admin.marketing.testDoneDesc": "{email}에서 메일 수신",
+ "admin.marketing.testDoneTitle": "테스트 발송 완료",
+ "admin.marketing.testEmail": "테스트 이메일 (선택)",
+ "admin.marketing.testEmailPlaceholder": "미입력 시: SMTP 발신 주소",
+ "admin.marketing.testFailedTitle": "테스트 발송 실패",
+ "admin.marketing.testRequiredHint": "실제 발송에는 동일 내용의 사전 테스트 발송이 필요합니다(서버가 검증).",
+ "admin.marketing.testValidated": "이 내용의 테스트 발송이 검증되었습니다.",
+ "admin.marketing.title": "마케팅 — 이메일 캠페인",
+ "admin.marketing.unsubscribedNote": "수신거부 {count}건은 모든 발송에서 제외됩니다.",
+ "admin.models.addModel": "모델 추가",
+ "admin.models.businessBadge": "Business 플랜",
+ "admin.models.cancel": "취소",
+ "admin.models.catalog": "OpenRouter 카탈로그",
+ "admin.models.catalogErrorDesc": "OpenRouter 카탈로그를 불러오지 못했습니다.",
+ "admin.models.catalogErrorTitle": "카탈로그 사용 불가",
+ "admin.models.customBadge": "맞춤 등급",
+ "admin.models.customNote": "맞춤 모델은 고객과 사례별로 정의합니다.",
+ "admin.models.defaultBadge": "기본",
+ "admin.models.emptyTier": "모델 없음: 플랜의 공식 라인업이 사용됩니다.",
+ "admin.models.enterpriseBadge": "Enterprise 플랜",
+ "admin.models.essentialBadge": "에센셜 AI 등급",
+ "admin.models.essentialCost": "— 비용 계수 1로 청구",
+ "admin.models.fallbackFirst": "(폴백 1순위)",
+ "admin.models.loadErrorDesc": "HTTP {status} — 관리자 토큰을 확인하세요.",
+ "admin.models.loadErrorTitle": "불러오기 오류",
+ "admin.models.matrixDesc": "등급별 활성 모델 — 목록 순서가 폴백 우선순위입니다. 라디오 버튼으로 기본 모델을 선택하면 재배포 없이 즉시 적용됩니다.",
+ "admin.models.matrixTitle": "플랜별 모델 매트릭스",
+ "admin.models.missingConfig": "AI 등급 구성을 찾을 수 없습니다. 페이지를 새로 고침하세요.",
+ "admin.models.moveDown": "아래로 이동",
+ "admin.models.moveUp": "위로 이동",
+ "admin.models.networkDesc": "백엔드에 연결할 수 없습니다.",
+ "admin.models.networkTitle": "네트워크 오류",
+ "admin.models.premiumBadge": "프리미엄 AI 등급",
+ "admin.models.premiumCost": "— 비용 계수 5로 청구",
+ "admin.models.premiumReservedNote": "Business·Enterprise 플랜 전용 («openrouter_premium» 엔진).",
+ "admin.models.proBadge": "Pro 플랜",
+ "admin.models.remove": "제거",
+ "admin.models.save": "저장",
+ "admin.models.saveErrorTitle": "저장 오류",
+ "admin.models.saveNetworkDesc": "구성을 저장할 수 없습니다.",
+ "admin.models.savedDesc": "새 기본 모델은 다음 번역부터 재배포 없이 적용됩니다.",
+ "admin.models.savedTitle": "모델 저장됨",
+ "admin.models.saving": "저장 중...",
+ "admin.models.setDefault": "{model}을(를) 기본 모델로 설정",
+ "admin.models.sharedTierNote": "공유 등급: Business 플랜도 «openrouter» 엔진에는 에센셜 등급을 사용합니다 — 위 목록은 두 플랜 모두에 적용됩니다.",
+ "admin.models.subtitle": "플랜 → AI 등급 매트릭스. 번역 시 실제 사용되는 모델은 플랜 등급의 모델입니다: Pro 플랜은 프리미엄 모델을 절대 호출할 수 없습니다.",
+ "admin.models.title": "모델 & 구독",
+ "admin.stats.aiTiers": "AI 등급 (30일)",
+ "admin.stats.includingCredits": "크레딧 {amount} 포함",
+ "admin.stats.mrr": "추정 MRR",
+ "admin.stats.noTranslationsYet": "최근 30일간 번역 기록이 없습니다: 앞으로의 번역으로 등급별 분포가 채워집니다.",
+ "admin.stats.payments30": "30일간 결제 {count}건",
+ "admin.stats.refreshing": "새로 고치는 중...",
+ "admin.stats.revenue30": "매출 (30일)",
+ "admin.stats.revenueTotal": "총 수령 매출",
+ "admin.stats.tiersSub": "에센셜 / 프리미엄 · 클래식 {classic} · 기타 {other}",
+ "admin.stats.unavailable": "비즈니스 통계를 사용할 수 없습니다 ({error}).",
+ "admin.stats.waitlist": "대기 명단",
+ "admin.stats.waitlistSub": "대기 중인 등록자"
}
diff --git a/frontend/src/lib/i18n/messages/ko/landing.json b/frontend/src/lib/i18n/messages/ko/landing.json
index c8d13e9..1f44557 100644
--- a/frontend/src/lib/i18n/messages/ko/landing.json
+++ b/frontend/src/lib/i18n/messages/ko/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "전문가용",
"landing.pricing.pro.f1": "월 200개 문서",
"landing.pricing.pro.f2": "문서당 최대 200페이지",
- "landing.pricing.pro.f3": "AI 번역",
+ "landing.pricing.pro.f3": "에센셜 AI: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google 포함",
"landing.pricing.pro.f5": "맞춤 용어집 및 프롬프트",
"landing.pricing.pro.f6": "우선 지원",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "대용량이 필요한 팀용",
"landing.pricing.business.f1": "월 1,000개 문서",
"landing.pricing.business.f2": "문서당 최대 500페이지",
- "landing.pricing.business.f3": "프리미엄 AI (Claude)",
+ "landing.pricing.business.f3": "프리미엄 AI (Claude 5)",
"landing.pricing.business.f4": "모든 제공자 + API",
"landing.pricing.business.f5": "웹훅 및 자동화",
"landing.pricing.business.f6": "팀 시트 5석",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "DOCX, XLSX, PPTX 또는 PDF 파일 지원",
"landing.translate.aiAnalysis": "AI 분석 활성",
"landing.translate.processing": "처리 중",
- "landing.translate.preservingLayout": "레이아웃이 보존되고 있습니다"
+ "landing.translate.preservingLayout": "레이아웃이 보존되고 있습니다",
+ "landing.beforeAfter.seal": "동일한 레이아웃, 단어 하나까지 그대로",
+ "landing.beforeAfter.proof1": "SmartArt 다이어그램은 병합되지 않고 재구성됩니다",
+ "landing.beforeAfter.proof2": "차트 계열과 축 번역",
+ "landing.beforeAfter.proof3": "목차는 대상 언어로 재생성",
+ "landing.beforeAfter.targetTitle": "기술 사양 — 공기 처리",
+ "landing.beforeAfter.targetPara1": "환기 설비는 2분기 종료 전에 가동되어야 합니다.",
+ "landing.beforeAfter.targetPara2Before": "",
+ "landing.beforeAfter.targetTerm": "공기조화기",
+ "landing.beforeAfter.targetPara2After": "는 일정에 따라 F7 필터 등급을 준수해야 합니다.",
+ "landing.beforeAfter.targetItem1": "열부하: 정격 유량에서 42kW",
+ "landing.beforeAfter.targetItem2": "소음 수준: 3m에서 45dB(A) 미만",
+ "landing.formats.pill": "호환성",
+ "landing.hero.visualCaption": "동일한 레이아웃, 새로운 언어 — 그 외에는 아무것도 바뀌지 않습니다",
+ "landing.pricing.free.name": "무료",
+ "landing.pricing.free.desc": "앱을 경험하기에 적합",
+ "landing.pricing.free.cta": "이 플랜 선택",
+ "landing.pricing.enterprise.name": "엔터프라이즈",
+ "landing.pricing.enterprise.desc": "대규모 조직을 위한 맞춤 솔루션",
+ "landing.pricing.enterprise.cta": "문의하기",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/ko/pricing.json b/frontend/src/lib/i18n/messages/ko/pricing.json
index 370377d..a749137 100644
--- a/frontend/src/lib/i18n/messages/ko/pricing.json
+++ b/frontend/src/lib/i18n/messages/ko/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "90일 기록",
"pricing.plans.business.feat1": "월 1,000개 문서",
"pricing.plans.business.feat2": "문서당 최대 500페이지",
- "pricing.plans.business.feat3": "에센셜 + 프리미엄 AI (Claude Haiku)",
+ "pricing.plans.business.feat3": "에센셜 + 프리미엄 AI (Claude 5)",
"pricing.plans.business.feat4": "모든 번역 제공업체",
"pricing.plans.business.feat5": "최대 50 MB 파일",
"pricing.plans.business.feat6": "API 액세스 (월 10,000회 호출)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "1년 기록",
"pricing.plans.business.feat10": "고급 분석",
"pricing.plans.enterprise.feat1": "무제한 문서",
- "pricing.plans.enterprise.feat2": "모든 AI 모델 (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "모든 AI 모델 (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "온프레미스 또는 전용 클라우드 배포",
"pricing.plans.enterprise.feat4": "99.9% SLA 보장",
"pricing.plans.enterprise.feat5": "24/7 전담 지원",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "초고속 멀티스레드 AI",
"pricing.trust.availability.title": "24/7 이용 가능",
"pricing.trust.availability.sub": "99.9% 가동 보장",
- "pricing.aiModels.title": "AI 모델 — 2026년 3월",
+ "pricing.aiModels.title": "AI 모델 — 2026년 9월",
"pricing.aiModels.essential.title": "에센셜 AI 번역",
"pricing.aiModels.essential.plan": "Pro 플랜",
"pricing.aiModels.essential.descPrefix": "기반:",
- "pricing.aiModels.essential.descSuffix": "— 2026년 가장 비용 효율적인 AI 모델. 프론티어 모델과 동등한 품질을 극소수의 비용으로 제공.",
- "pricing.aiModels.essential.modelName": "Essential AI 모델",
- "pricing.aiModels.essential.context": "163K 토큰 컨텍스트",
- "pricing.aiModels.essential.value": "우수한 가성비",
+ "pricing.aiModels.essential.descSuffix": "— 2026년 가장 비용 효율적인 AI 모델.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
+ "pricing.aiModels.essential.context": "최대 130만 토큰 컨텍스트 (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "최고의 가성비",
"pricing.aiModels.premium.title": "프리미엄 AI 번역",
"pricing.aiModels.premium.plan": "Business 플랜",
"pricing.aiModels.premium.descPrefix": "기반:",
"pricing.aiModels.premium.descSuffix": "Anthropic사 제품 — 법률, 의료 및 복잡한 기술 문서에서 높은 정확도.",
- "pricing.aiModels.premium.context": "200K 토큰 컨텍스트",
+ "pricing.aiModels.premium.context": "100만 토큰 컨텍스트",
"pricing.aiModels.premium.precision": "최고 정확도",
"pricing.faq.title": "자주 묻는 질문",
"pricing.faq.q1": "언제든지 플랜을 변경할 수 있나요?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "「에센셜 AI 번역」이란 무엇인가요?",
"pricing.faq.a2": "당사의 AI 엔진입니다. 문서의 맥락을 이해하고, 레이아웃을 보존하며, 기술 용어를 기존 번역보다 훨씬 잘 처리합니다.",
"pricing.faq.q3": "에센셜 AI와 프리미엄 AI의 차이점은 무엇인가요?",
- "pricing.faq.a3": "Essential AI는 최적화된 모델을 사용합니다 (우수한 가성비). Premium AI는 Anthropic의 Claude 3.5 Haiku를 사용하여 법률, 의료 및 복잡한 기술 문서에서 더 정확합니다.",
+ "pricing.faq.a3": "Essential AI는 DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3를 사용합니다 (우수한 가성비). Premium AI는 Anthropic의 Claude 5를 사용하여 법률, 의료 및 복잡한 기술 문서에서 더 정확합니다.",
"pricing.faq.q4": "번역 후 문서가 보관되나요?",
"pricing.faq.a4": "번역된 파일은 플랜에 따라 이용 가능합니다 (Starter 30일, Pro 90일, Business 1년). 저장 시 및 전송 중 암호화됩니다.",
"pricing.faq.q5": "월간 할당량을 초과하면 어떻게 되나요?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "결제 생성 중 오류가 발생했습니다.",
"pricing.dashboard": "대시보드",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "백만 토큰당 $0.09부터",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "대체 모델: DeepSeek V4 Pro, GLM-5.3",
+ "pricing.confirm.title": "구독 확인",
+ "pricing.confirm.subtitle": "안전한 결제 대행사로 이동합니다.",
+ "pricing.confirm.cancel": "취소",
+ "pricing.confirm.cta": "결제로 계속",
+ "pricing.confirm.year": "년",
+ "pricing.confirm.month": "개월",
+ "pricing.confirm.monthlyEquivalent": "연간 결제 — 월 {price}유로 상당.",
+ "pricing.confirm.secureNote": "프로필에서 언제든지 해지할 수 있습니다. 결제는 Stripe에서 처리되며 카드 정보는 당사 서버에 저장되지 않습니다.",
+ "pricing.enterprise.subject": "엔터프라이즈 문의",
+ "pricing.enterpriseBand.cta": "문의하기",
+ "pricing.enterpriseBand.text": "대용량, 전용 엔진, 온프레미스 옵션 — 상담해 보세요.",
+ "pricing.error.server": "서버 오류 {status}",
+ "pricing.freeBand.cta": "무료로 시작",
+ "pricing.freeBand.text": "일단 써보고 싶으신가요? 무료로 시작 — 월 5개 문서, 카드 불필요.",
+ "pricing.header.titleBase": "모든 필요를 위한",
+ "pricing.header.titleAccent": "하나의 플랜",
+ "pricing.toast.close": "닫기",
+ "pricing.aiModels.premium.price": "백만 토큰당 $2 / $10"
}
diff --git a/frontend/src/lib/i18n/messages/ko/providerTheme.json b/frontend/src/lib/i18n/messages/ko/providerTheme.json
index 0b296a0..6106431 100644
--- a/frontend/src/lib/i18n/messages/ko/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/ko/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "번역에 최적화된 최고의 오픈소스 모델에 대한 통합 액세스.",
"providerTheme.openrouter_premium.badge": "울트라",
"providerTheme.openrouter_premium.subBadge": "최대 컨텍스트",
- "providerTheme.openrouter_premium.desc": "최신 모델(GPT-4o, Claude Sonnet 4.6)의 지원을 받는 긴 문서 번역.",
+ "providerTheme.openrouter_premium.desc": "최신 모델(GPT-4o, Claude 5)의 지원을 받는 긴 문서 번역.",
"providerTheme.zai.badge": "전문",
"providerTheme.zai.subBadge": "금융 및 법률",
"providerTheme.zai.desc": "까다로운 비즈니스 용어(법률, 금융)에 맞게 미세 조정된 모델.",
diff --git a/frontend/src/lib/i18n/messages/nl/admin.json b/frontend/src/lib/i18n/messages/nl/admin.json
index 53071b2..fe58eee 100644
--- a/frontend/src/lib/i18n/messages/nl/admin.json
+++ b/frontend/src/lib/i18n/messages/nl/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Providers",
"admin.nav.system": "Systeem",
"admin.nav.logs": "Logboeken",
+ "admin.nav.stats": "Statistieken",
"admin.users.title": "Gebruikersbeheer",
"admin.users.subtitle": "Gebruikersaccounts bekijken en beheren",
"admin.users.planUpdated": "Abonnement bijgewerkt",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "Wachten op gegevens...",
"admin.system.purging": "Wissen...",
"admin.system.clean": "Opschonen",
- "admin.system.purge": "Wissen"
+ "admin.system.purge": "Wissen",
+ "admin.nav.models": "Modellen & abonnementen",
+ "admin.nav.marketing": "Marketing",
+ "admin.marketing.audience": "Doelgroep",
+ "admin.marketing.audienceAllUsers": "Alle accounts",
+ "admin.marketing.audienceInactive": "Accounts 30 dagen inactief",
+ "admin.marketing.audiencePlaceholder": "Kies een doelgroep",
+ "admin.marketing.audiencePlanBusiness": "Business-abonnement",
+ "admin.marketing.audiencePlanEnterprise": "Enterprise-abonnement",
+ "admin.marketing.audiencePlanFree": "Gratis-abonnement",
+ "admin.marketing.audiencePlanPro": "Pro-abonnement",
+ "admin.marketing.audiencePlanStarter": "Starter-abonnement",
+ "admin.marketing.audienceWaitlist": "Wachtlijst",
+ "admin.marketing.badgeReal": "echt",
+ "admin.marketing.badgeTest": "test",
+ "admin.marketing.confirmSend": "Deze e-mail versturen naar {count} ontvanger(s) („{audience}”)?",
+ "admin.marketing.footerNote": "Een voettekst met de uitschrijflink wordt automatisch toegevoegd bij verzending.",
+ "admin.marketing.hidePreview": "Voorbeeld verbergen",
+ "admin.marketing.history": "Verzendgeschiedenis",
+ "admin.marketing.historyFailedSuffix": ", {count} mislukt",
+ "admin.marketing.historySent": "{sent}/{total} verzonden",
+ "admin.marketing.html": "HTML-inhoud",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Vul het onderwerp en de HTML in.",
+ "admin.marketing.incompleteTitle": "Onvolledige inhoud",
+ "admin.marketing.loadErrorTitle": "Laadfout",
+ "admin.marketing.localCheckUnavailable": "Lokale controle niet beschikbaar in deze browser: de server controleert de voorafgaande testzending.",
+ "admin.marketing.networkDesc": "Kan de backend niet bereiken.",
+ "admin.marketing.newCampaign": "Nieuwe campagne",
+ "admin.marketing.noHistory": "Geen zending geregistreerd.",
+ "admin.marketing.preview": "Voorbeeld",
+ "admin.marketing.previewEmpty": "Voorbeeld: plak je HTML hiernaast.",
+ "admin.marketing.previewTitle": "E-mailvoorbeeld",
+ "admin.marketing.recipientsAvailable": "{count} ontvanger(s) beschikbaar na uitsluiting van uitschrijvingen.",
+ "admin.marketing.sendReal": "Versturen naar {count} ontvanger(s)",
+ "admin.marketing.sendRefusedTitle": "Verzending geweigerd",
+ "admin.marketing.sendTest": "Testzending",
+ "admin.marketing.sendingDesc": "{count} e-mail(s) in de verzendwachtrij (interval 0,2 s). Uitschrijvingen worden uitgesloten.",
+ "admin.marketing.sendingTitle": "Verzending bezig",
+ "admin.marketing.subject": "Onderwerp",
+ "admin.marketing.subjectPlaceholder": "Bijv.: Uw vertaling wacht — 20% gedurende 7 dagen",
+ "admin.marketing.subtitle": "E-mails sturen naar een doelgroep, met verplichte testzending, automatische uitschrijflink en volledige geschiedenis.",
+ "admin.marketing.testDoneDesc": "E-mail ontvangen op {email}",
+ "admin.marketing.testDoneTitle": "Testzending uitgevoerd",
+ "admin.marketing.testEmail": "Test-e-mail (optioneel)",
+ "admin.marketing.testEmailPlaceholder": "Anders: SMTP-afzenderadres",
+ "admin.marketing.testFailedTitle": "Testzending mislukt",
+ "admin.marketing.testRequiredHint": "Een echte zending vereist een voorafgaande testzending van exact deze inhoud (door de server gecontroleerd).",
+ "admin.marketing.testValidated": "Testzending gevalideerd voor deze inhoud.",
+ "admin.marketing.title": "Marketing — e-mailcampagnes",
+ "admin.marketing.unsubscribedNote": "{count} uitgeschreven contact(en) worden van elke zending uitgesloten.",
+ "admin.models.addModel": "Model toevoegen",
+ "admin.models.businessBadge": "Business-abonnement",
+ "admin.models.cancel": "Annuleren",
+ "admin.models.catalog": "OpenRouter-catalogus",
+ "admin.models.catalogErrorDesc": "OpenRouter-catalogus kon niet worden geladen.",
+ "admin.models.catalogErrorTitle": "Catalogus niet beschikbaar",
+ "admin.models.customBadge": "Aangepast niveau",
+ "admin.models.customNote": "Maatwerkmodellen, per geval met de klant afgesproken.",
+ "admin.models.defaultBadge": "standaard",
+ "admin.models.emptyTier": "Geen model: de officiële modellenreeks van het abonnement wordt gebruikt.",
+ "admin.models.enterpriseBadge": "Enterprise-abonnement",
+ "admin.models.essentialBadge": "Basis-AI-niveau",
+ "admin.models.essentialCost": "— gefactoreerd met kostenfactor 1",
+ "admin.models.fallbackFirst": "(fallback nr. 1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — controleer je admin-token.",
+ "admin.models.loadErrorTitle": "Laadfout",
+ "admin.models.matrixDesc": "Actieve modellen per niveau — de volgorde van de lijst is de fallback-prioriteit. De keuzerondje kiest het standaardmodel, direct actief zonder heruitrol.",
+ "admin.models.matrixTitle": "Modelmatrix per abonnement",
+ "admin.models.missingConfig": "AI-niveauconfiguratie niet gevonden. Herlaad de pagina.",
+ "admin.models.moveDown": "Omlaag",
+ "admin.models.moveUp": "Omhoog",
+ "admin.models.networkDesc": "Kan de backend niet bereiken.",
+ "admin.models.networkTitle": "Netwerkfout",
+ "admin.models.premiumBadge": "Premium-AI-niveau",
+ "admin.models.premiumCost": "— gefactoreerd met kostenfactor 5",
+ "admin.models.premiumReservedNote": "Voorbehouden aan Business- en Enterprise-abonnementen (engine «openrouter_premium»).",
+ "admin.models.proBadge": "Pro-abonnement",
+ "admin.models.remove": "Verwijderen",
+ "admin.models.save": "Opslaan",
+ "admin.models.saveErrorTitle": "Opslagfout",
+ "admin.models.saveNetworkDesc": "Configuratie kon niet worden opgeslagen.",
+ "admin.models.savedDesc": "Het nieuwe standaardmodel wordt vanaf de volgende vertaling gebruikt, zonder heruitrol.",
+ "admin.models.savedTitle": "Modellen opgeslagen",
+ "admin.models.saving": "Opslaan...",
+ "admin.models.setDefault": "{model} als standaardmodel instellen",
+ "admin.models.sharedTierNote": "Gedeeld niveau: het Business-abonnement gebruikt het Basis-niveau ook voor zijn «openrouter»-engine — de bovenstaande lijst geldt voor beide abonnementen.",
+ "admin.models.subtitle": "Matrix abonnementen → AI-niveaus. Het model dat per vertaling echt wordt gebruikt is dat van het niveau van het abonnement: een Pro-abonnement kan nooit een Premium-model aanroepen.",
+ "admin.models.title": "Modellen & abonnementen",
+ "admin.stats.aiTiers": "AI-niveaus (30 d)",
+ "admin.stats.includingCredits": "waarvan {amount} aan credits",
+ "admin.stats.mrr": "Geschatte MRR",
+ "admin.stats.noTranslationsYet": "Geen vertalingen van de laatste 30 dagen in de database: de niveauverdeling vult zich aan met de komende vertalingen.",
+ "admin.stats.payments30": "{count} betaling(en) in 30 dagen",
+ "admin.stats.refreshing": "Vernieuwen...",
+ "admin.stats.revenue30": "Omzet (30 dagen)",
+ "admin.stats.revenueTotal": "Totaal ontvangen omzet",
+ "admin.stats.tiersSub": "Basis / Premium · klassiek {classic} · overig {other}",
+ "admin.stats.unavailable": "Bedrijfsstatistieken niet beschikbaar ({error}).",
+ "admin.stats.waitlist": "Wachtlijst",
+ "admin.stats.waitlistSub": "inschrijvingen op de wachtlijst"
}
diff --git a/frontend/src/lib/i18n/messages/nl/landing.json b/frontend/src/lib/i18n/messages/nl/landing.json
index 06ba636..e145b2a 100644
--- a/frontend/src/lib/i18n/messages/nl/landing.json
+++ b/frontend/src/lib/i18n/messages/nl/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "Voor veeleisende professionals",
"landing.pricing.pro.f1": "200 documenten / maand",
"landing.pricing.pro.f2": "Tot 200 pagina's per doc",
- "landing.pricing.pro.f3": "IA-aangedreven vertaling",
+ "landing.pricing.pro.f3": "Basis-AI: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google inbegrepen",
"landing.pricing.pro.f5": "Eigen glossaria en prompts",
"landing.pricing.pro.f6": "Prioriteitsondersteuning",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "Voor teams met grote behoeften",
"landing.pricing.business.f1": "1 000 documenten / maand",
"landing.pricing.business.f2": "Tot 500 pagina's per doc",
- "landing.pricing.business.f3": "Premium AI (Claude)",
+ "landing.pricing.business.f3": "Premium-AI (Claude 5)",
"landing.pricing.business.f4": "Alle providers + API-toegang",
"landing.pricing.business.f5": "Webhooks en automatisering",
"landing.pricing.business.f6": "5 teamplekken",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "DOCX, XLSX, PPTX of PDF bestanden ondersteund",
"landing.translate.aiAnalysis": "Actieve AI-analyse",
"landing.translate.processing": "Verwerking bezig",
- "landing.translate.preservingLayout": "Uw opmaak wordt behouden"
+ "landing.translate.preservingLayout": "Uw opmaak wordt behouden",
+ "landing.beforeAfter.seal": "Zelfde lay-out, woord voor woord",
+ "landing.beforeAfter.proof1": "SmartArt-diagrammen worden herbouwd, niet afgevlakt",
+ "landing.beforeAfter.proof2": "Diagramreeksen en assen vertaald",
+ "landing.beforeAfter.proof3": "Inhoudsopgaven opnieuw gegenereerd in de doeltaal",
+ "landing.beforeAfter.targetTitle": "Technische specificatie — Luchtbehandeling",
+ "landing.beforeAfter.targetPara1": "Het ventilatiesysteem moet vóór het einde van het tweede kwartaal in bedrijf worden gesteld.",
+ "landing.beforeAfter.targetPara2Before": "De",
+ "landing.beforeAfter.targetTerm": "luchtbehandelingsgroep",
+ "landing.beforeAfter.targetPara2After": "moet volgens planning voldoen aan filterklasse F7.",
+ "landing.beforeAfter.targetItem1": "Thermische belasting: 42 kW bij nominale doorstroom",
+ "landing.beforeAfter.targetItem2": "Geluidsniveau onder 45 dB(A) op 3 m",
+ "landing.formats.pill": "COMPATIBILITEIT",
+ "landing.hero.visualCaption": "Zelfde lay-out, nieuwe taal — verder verandert niets",
+ "landing.pricing.free.name": "Gratis",
+ "landing.pricing.free.desc": "Ideaal om de app te ontdekken",
+ "landing.pricing.free.cta": "Dit plan kiezen",
+ "landing.pricing.enterprise.name": "Enterprise",
+ "landing.pricing.enterprise.desc": "Op maat gemaakte oplossingen voor grote organisaties",
+ "landing.pricing.enterprise.cta": "Neem contact op",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/nl/pricing.json b/frontend/src/lib/i18n/messages/nl/pricing.json
index 72e6c58..5e41bcf 100644
--- a/frontend/src/lib/i18n/messages/nl/pricing.json
+++ b/frontend/src/lib/i18n/messages/nl/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "90 dagen geschiedenis",
"pricing.plans.business.feat1": "1 000 documenten / maand",
"pricing.plans.business.feat2": "Tot 500 pagina's per document",
- "pricing.plans.business.feat3": "Basis- + Premium-AI (Claude Haiku)",
+ "pricing.plans.business.feat3": "Basis-AI + Premium-AI (Claude 5)",
"pricing.plans.business.feat4": "Alle vertaalproviders",
"pricing.plans.business.feat5": "Bestanden tot 50 MB",
"pricing.plans.business.feat6": "API-toegang (10 000 aanroepen/maand)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "1 jaar geschiedenis",
"pricing.plans.business.feat10": "Geavanceerde analyses",
"pricing.plans.enterprise.feat1": "Onbeperkte documenten",
- "pricing.plans.enterprise.feat2": "Alle AI-modellen (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "Alle AI-modellen (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "On-premise of dedicated cloud",
"pricing.plans.enterprise.feat4": "99,9 % SLA gegarandeerd",
"pricing.plans.enterprise.feat5": "24/7 dedicated support",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "Ultrasnelle multi-threaded AI",
"pricing.trust.availability.title": "24/7 beschikbaar",
"pricing.trust.availability.sub": "99,9 % gegarandeerde beschikbaarheid",
- "pricing.aiModels.title": "Onze AI-modellen — maart 2026",
+ "pricing.aiModels.title": "Onze AI-modellen — september 2026",
"pricing.aiModels.essential.title": "AI-basisvertaling",
"pricing.aiModels.essential.plan": "Pro-abonnement",
"pricing.aiModels.essential.descPrefix": "Gebaseerd op",
- "pricing.aiModels.essential.descSuffix": "— het meest kostenefficiënte AI-model van 2026. Kwaliteit vergelijkbaar met frontier-modellen voor een fractie van de kosten.",
- "pricing.aiModels.essential.modelName": "ons Essential AI-model",
- "pricing.aiModels.essential.context": "163K tokens context",
- "pricing.aiModels.essential.value": "Uitstekende prijs-kwaliteitverhouding",
+ "pricing.aiModels.essential.descSuffix": "— de AI-modellen met de beste prijs-kwaliteitverhouding van 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash en MiniMax M3",
+ "pricing.aiModels.essential.context": "Tot 1,3 mln context (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "De beste prijs-kwaliteitverhouding",
"pricing.aiModels.premium.title": "AI-premiumvertaling",
"pricing.aiModels.premium.plan": "Business-abonnement",
"pricing.aiModels.premium.descPrefix": "Gebaseerd op",
"pricing.aiModels.premium.descSuffix": "van Anthropic — nauwkeurig bij juridische, medische en complexe technische documenten.",
- "pricing.aiModels.premium.context": "200K tokens context",
+ "pricing.aiModels.premium.context": "1 mln context",
"pricing.aiModels.premium.precision": "Beste nauwkeurigheid",
"pricing.faq.title": "Veelgestelde vragen",
"pricing.faq.q1": "Kan ik op elk moment van abonnement wisselen?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "Wat is «AI-basisvertaling»?",
"pricing.faq.a2": "Het is onze IA-engine. Hij begrijpt de context van uw documenten, behoudt de lay-out en behandelt technische termen veel beter dan klassieke vertaling.",
"pricing.faq.q3": "Wat is het verschil tussen Basis- en Premium-AI?",
- "pricing.faq.a3": "Essential IA gebruikt een geoptimaliseerd model (uitstekende prijs-kwaliteitverhouding). Premium IA gebruikt Claude 3.5 Haiku van Anthropic, nauwkeuriger bij juridische, medische en complexe technische documenten.",
+ "pricing.faq.a3": "Basis-AI draait op DeepSeek V4 Flash, GLM-5.3 Flash en MiniMax M3 (uitstekende prijs-kwaliteitverhouding). Premium-AI gebruikt Claude 5 van Anthropic, nauwkeuriger bij juridische, medische en complexe technische documenten.",
"pricing.faq.q4": "Worden mijn documenten bewaard na vertaling?",
"pricing.faq.a4": "Vertaalde bestanden zijn beschikbaar volgens uw abonnement (30 dagen Starter, 90 dagen Pro, 1 jaar Business). Ze zijn versleuteld at-rest en in transit.",
"pricing.faq.q5": "Wat gebeurt er als ik mijn maandelijkse quotum overschrijd?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "Fout bij aanmaken van de betaling.",
"pricing.dashboard": "Dashboard",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "Vanaf $0,09 per miljoen tokens",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Alternatieven: DeepSeek V4 Pro en GLM-5.3",
+ "pricing.confirm.title": "Bevestig uw abonnement",
+ "pricing.confirm.subtitle": "U wordt doorgestuurd naar onze veilige betaalprovider.",
+ "pricing.confirm.cancel": "Annuleren",
+ "pricing.confirm.cta": "Verder naar betaling",
+ "pricing.confirm.year": "jaar",
+ "pricing.confirm.month": "maand",
+ "pricing.confirm.monthlyEquivalent": "Jaarlijkse facturering — dus {price} € / maand.",
+ "pricing.confirm.secureNote": "Op elk moment opzegbaar via uw profiel. Betaling wordt verwerkt door Stripe; uw kaartgegevens raken onze servers nooit.",
+ "pricing.enterprise.subject": "Enterprise-aanvraag",
+ "pricing.enterpriseBand.cta": "Neem contact op",
+ "pricing.enterpriseBand.text": "Volume, speciale engines, on-premise opties — laten we praten.",
+ "pricing.error.server": "Serverfout {status}",
+ "pricing.freeBand.cta": "Gratis starten",
+ "pricing.freeBand.text": "Eerst uitproberen? Start gratis — 5 documenten per maand, geen kaart nodig.",
+ "pricing.header.titleBase": "Een plan voor",
+ "pricing.header.titleAccent": "elke behoefte",
+ "pricing.toast.close": "Sluiten",
+ "pricing.aiModels.premium.price": "$2 / $10 per 1M tokens"
}
diff --git a/frontend/src/lib/i18n/messages/nl/providerTheme.json b/frontend/src/lib/i18n/messages/nl/providerTheme.json
index d7f81f0..c270f8f 100644
--- a/frontend/src/lib/i18n/messages/nl/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/nl/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Uniforme toegang tot de beste open-source modellen, geoptimaliseerd voor vertaling.",
"providerTheme.openrouter_premium.badge": "Ultra",
"providerTheme.openrouter_premium.subBadge": "Maximale context",
- "providerTheme.openrouter_premium.desc": "Ondersteund door state-of-the-art modellen (GPT-4o, Claude Sonnet 4.6) voor lange documenten.",
+ "providerTheme.openrouter_premium.desc": "Ondersteund door state-of-the-art modellen (GPT-4o, Claude 5) voor lange documenten.",
"providerTheme.zai.badge": "Gespecialiseerd",
"providerTheme.zai.subBadge": "Financiën & Recht",
"providerTheme.zai.desc": "Model afgestemd op veeleisende bedrijfsterminologieën (juridisch, financieel).",
diff --git a/frontend/src/lib/i18n/messages/pt/admin.json b/frontend/src/lib/i18n/messages/pt/admin.json
index 38e343e..492b0de 100644
--- a/frontend/src/lib/i18n/messages/pt/admin.json
+++ b/frontend/src/lib/i18n/messages/pt/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Provedores",
"admin.nav.system": "Sistema",
"admin.nav.logs": "Registros",
+ "admin.nav.stats": "Estatísticas",
"admin.users.title": "Gestão de Usuários",
"admin.users.subtitle": "Visualizar e gerenciar contas de usuários",
"admin.users.planUpdated": "Plano atualizado",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "Aguardando dados...",
"admin.system.purging": "Limpando...",
"admin.system.clean": "Limpar",
- "admin.system.purge": "Limpar"
+ "admin.system.purge": "Limpar",
+ "admin.nav.models": "Modelos e assinaturas",
+ "admin.nav.marketing": "Marketing",
+ "admin.marketing.audience": "Público",
+ "admin.marketing.audienceAllUsers": "Todas as contas",
+ "admin.marketing.audienceInactive": "Contas inativas há 30 dias",
+ "admin.marketing.audiencePlaceholder": "Escolher um público",
+ "admin.marketing.audiencePlanBusiness": "Plano Business",
+ "admin.marketing.audiencePlanEnterprise": "Plano Enterprise",
+ "admin.marketing.audiencePlanFree": "Plano Gratuito",
+ "admin.marketing.audiencePlanPro": "Plano Pro",
+ "admin.marketing.audiencePlanStarter": "Plano Starter",
+ "admin.marketing.audienceWaitlist": "Lista de espera",
+ "admin.marketing.badgeReal": "real",
+ "admin.marketing.badgeTest": "teste",
+ "admin.marketing.confirmSend": "Enviar este email para {count} destinatário(s) («{audience}»)?",
+ "admin.marketing.footerNote": "Um rodapé com o link de descadastro é adicionado automaticamente ao envio.",
+ "admin.marketing.hidePreview": "Ocultar pré-visualização",
+ "admin.marketing.history": "Histórico de envios",
+ "admin.marketing.historyFailedSuffix": ", {count} falhas",
+ "admin.marketing.historySent": "{sent}/{total} enviados",
+ "admin.marketing.html": "Conteúdo HTML",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Preencha o assunto e o HTML.",
+ "admin.marketing.incompleteTitle": "Conteúdo incompleto",
+ "admin.marketing.loadErrorTitle": "Erro ao carregar",
+ "admin.marketing.localCheckUnavailable": "Verificação local indisponível neste navegador: o servidor verificará o envio de teste prévio.",
+ "admin.marketing.networkDesc": "Não foi possível contatar o backend.",
+ "admin.marketing.newCampaign": "Novo envio",
+ "admin.marketing.noHistory": "Nenhum envio registrado.",
+ "admin.marketing.preview": "Pré-visualização",
+ "admin.marketing.previewEmpty": "Pré-visualização: cole seu HTML ao lado.",
+ "admin.marketing.previewTitle": "Pré-visualização do email",
+ "admin.marketing.recipientsAvailable": "{count} destinatário(s) disponíveis após excluir os descadastrados.",
+ "admin.marketing.sendReal": "Enviar para {count} destinatário(s)",
+ "admin.marketing.sendRefusedTitle": "Envio recusado",
+ "admin.marketing.sendTest": "Envio de teste",
+ "admin.marketing.sendingDesc": "{count} email(s) na fila de envio (intervalo de 0,2 s). Os descadastrados ficam excluídos.",
+ "admin.marketing.sendingTitle": "Envio em andamento",
+ "admin.marketing.subject": "Assunto",
+ "admin.marketing.subjectPlaceholder": "Ex.: Sua tradução o espera — 20% por 7 dias",
+ "admin.marketing.subtitle": "Envio de emails para um público, com envio de teste obrigatório, link de descadastro automático e histórico completo.",
+ "admin.marketing.testDoneDesc": "Email recebido em {email}",
+ "admin.marketing.testDoneTitle": "Envio de teste realizado",
+ "admin.marketing.testEmail": "Email de teste (opcional)",
+ "admin.marketing.testEmailPlaceholder": "Caso contrário: endereço de remetente SMTP",
+ "admin.marketing.testFailedTitle": "Falha no envio de teste",
+ "admin.marketing.testRequiredHint": "O envio real exige um envio de teste prévio deste conteúdo exato (verificado pelo servidor).",
+ "admin.marketing.testValidated": "Envio de teste validado para este conteúdo.",
+ "admin.marketing.title": "Marketing — envios por email",
+ "admin.marketing.unsubscribedNote": "{count} contato(s) descadastrado(s) serão excluídos de todo envio.",
+ "admin.models.addModel": "Adicionar um modelo",
+ "admin.models.businessBadge": "Plano Business",
+ "admin.models.cancel": "Cancelar",
+ "admin.models.catalog": "Catálogo do OpenRouter",
+ "admin.models.catalogErrorDesc": "Não foi possível carregar o catálogo do OpenRouter.",
+ "admin.models.catalogErrorTitle": "Catálogo indisponível",
+ "admin.models.customBadge": "Nível personalizado",
+ "admin.models.customNote": "Modelos sob medida, definidos caso a caso com o cliente.",
+ "admin.models.defaultBadge": "padrão",
+ "admin.models.emptyTier": "Nenhum modelo: a gama oficial do plano será usada.",
+ "admin.models.enterpriseBadge": "Plano Enterprise",
+ "admin.models.essentialBadge": "Nível de IA Essencial",
+ "admin.models.essentialCost": "— faturado com fator de custo 1",
+ "admin.models.fallbackFirst": "(reserva n.º 1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — verifique seu token de administrador.",
+ "admin.models.loadErrorTitle": "Erro ao carregar",
+ "admin.models.matrixDesc": "Modelos ativos por nível — a ordem da lista é a prioridade de reserva. O botão de opção escolhe o modelo padrão, aplicado imediatamente sem nova implantação.",
+ "admin.models.matrixTitle": "Matriz de modelos por plano",
+ "admin.models.missingConfig": "Configuração dos níveis de IA não encontrada. Recarregue a página.",
+ "admin.models.moveDown": "Mover para baixo",
+ "admin.models.moveUp": "Mover para cima",
+ "admin.models.networkDesc": "Não foi possível contatar o backend.",
+ "admin.models.networkTitle": "Erro de rede",
+ "admin.models.premiumBadge": "Nível de IA Premium",
+ "admin.models.premiumCost": "— faturado com fator de custo 5",
+ "admin.models.premiumReservedNote": "Reservado aos planos Business e Enterprise (motor «openrouter_premium»).",
+ "admin.models.proBadge": "Plano Pro",
+ "admin.models.remove": "Remover",
+ "admin.models.save": "Salvar",
+ "admin.models.saveErrorTitle": "Erro ao salvar",
+ "admin.models.saveNetworkDesc": "Não foi possível salvar a configuração.",
+ "admin.models.savedDesc": "O novo modelo padrão é usado já na próxima tradução, sem nova implantação.",
+ "admin.models.savedTitle": "Modelos salvos",
+ "admin.models.saving": "Salvando...",
+ "admin.models.setDefault": "Definir {model} como modelo padrão",
+ "admin.models.sharedTierNote": "Nível compartilhado: o plano Business também usa o Essencial para o motor «openrouter» — a lista acima vale para os dois planos.",
+ "admin.models.subtitle": "Matriz planos → níveis de IA. O modelo realmente usado em cada tradução é o do nível do plano: um plano Pro nunca pode disparar um modelo Premium.",
+ "admin.models.title": "Modelos e assinaturas",
+ "admin.stats.aiTiers": "Níveis de IA (30 d)",
+ "admin.stats.includingCredits": "incluindo {amount} em créditos",
+ "admin.stats.mrr": "MRR estimado",
+ "admin.stats.noTranslationsYet": "Nenhuma tradução registrada nos últimos 30 dias: a distribuição por níveis se preencherá com as próximas traduções.",
+ "admin.stats.payments30": "{count} pagamento(s) em 30 dias",
+ "admin.stats.refreshing": "Atualizando...",
+ "admin.stats.revenue30": "Receitas (30 dias)",
+ "admin.stats.revenueTotal": "Receitas recebidas (total)",
+ "admin.stats.tiersSub": "Essencial / Premium · clássico {classic} · outro {other}",
+ "admin.stats.unavailable": "Estatísticas de negócio indisponíveis ({error}).",
+ "admin.stats.waitlist": "Lista de espera",
+ "admin.stats.waitlistSub": "inscritos na espera"
}
diff --git a/frontend/src/lib/i18n/messages/pt/landing.json b/frontend/src/lib/i18n/messages/pt/landing.json
index 8a2f21b..ad3dfa0 100644
--- a/frontend/src/lib/i18n/messages/pt/landing.json
+++ b/frontend/src/lib/i18n/messages/pt/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "Para profissionais exigentes",
"landing.pricing.pro.f1": "200 documentos / mês",
"landing.pricing.pro.f2": "Até 200 páginas por doc",
- "landing.pricing.pro.f3": "Tradução com IA",
+ "landing.pricing.pro.f3": "IA Essencial: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google incluídos",
"landing.pricing.pro.f5": "Glossários e prompts personalizados",
"landing.pricing.pro.f6": "Suporte prioritário",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "Para equipes com alto volume",
"landing.pricing.business.f1": "1 000 documentos / mês",
"landing.pricing.business.f2": "Até 500 páginas por doc",
- "landing.pricing.business.f3": "IA Premium (Claude)",
+ "landing.pricing.business.f3": "IA Premium (Claude 5)",
"landing.pricing.business.f4": "Todos os provedores + API",
"landing.pricing.business.f5": "Webhooks e automação",
"landing.pricing.business.f6": "5 assentos de equipe",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "Ficheiros DOCX, XLSX, PPTX ou PDF suportados",
"landing.translate.aiAnalysis": "Análise IA Ativa",
"landing.translate.processing": "Em processamento",
- "landing.translate.preservingLayout": "O seu layout está a ser preservado"
+ "landing.translate.preservingLayout": "O seu layout está a ser preservado",
+ "landing.beforeAfter.seal": "O mesmo layout, palavra por palavra",
+ "landing.beforeAfter.proof1": "Diagramas SmartArt reconstruídos, não achatados",
+ "landing.beforeAfter.proof2": "Séries e eixos dos gráficos traduzidos",
+ "landing.beforeAfter.proof3": "Sumários regenerados no idioma de destino",
+ "landing.beforeAfter.targetTitle": "Especificação técnica — Tratamento de ar",
+ "landing.beforeAfter.targetPara1": "A instalação de ventilação deve ser colocada em serviço antes do fim do segundo trimestre.",
+ "landing.beforeAfter.targetPara2Before": "A",
+ "landing.beforeAfter.targetTerm": "unidade de tratamento de ar",
+ "landing.beforeAfter.targetPara2After": "deve atender à classe de filtro F7 conforme o cronograma.",
+ "landing.beforeAfter.targetItem1": "Carga térmica: 42 kW em vazão nominal",
+ "landing.beforeAfter.targetItem2": "Nível sonoro abaixo de 45 dB(A) a 3 m",
+ "landing.formats.pill": "COMPATIBILIDADE",
+ "landing.hero.visualCaption": "O mesmo layout, novo idioma — nada mais muda",
+ "landing.pricing.free.name": "Grátis",
+ "landing.pricing.free.desc": "Ideal para descobrir a aplicação",
+ "landing.pricing.free.cta": "Escolher este plano",
+ "landing.pricing.enterprise.name": "Empresas",
+ "landing.pricing.enterprise.desc": "Soluções à medida para grandes organizações",
+ "landing.pricing.enterprise.cta": "Fale conosco",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/pt/pricing.json b/frontend/src/lib/i18n/messages/pt/pricing.json
index 21ba511..a85e15f 100644
--- a/frontend/src/lib/i18n/messages/pt/pricing.json
+++ b/frontend/src/lib/i18n/messages/pt/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "Histórico de 90 dias",
"pricing.plans.business.feat1": "1 000 documentos / mês",
"pricing.plans.business.feat2": "Até 500 páginas por documento",
- "pricing.plans.business.feat3": "IA Essencial + Premium (Claude Haiku)",
+ "pricing.plans.business.feat3": "IA Essencial + IA Premium (Claude 5)",
"pricing.plans.business.feat4": "Todos os provedores de tradução",
"pricing.plans.business.feat5": "Arquivos de até 50 MB",
"pricing.plans.business.feat6": "Acesso à API (10 000 chamadas/mês)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "Histórico de 1 ano",
"pricing.plans.business.feat10": "Análises avançadas",
"pricing.plans.enterprise.feat1": "Documentos ilimitados",
- "pricing.plans.enterprise.feat2": "Todos os modelos de IA (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "Todos os modelos de IA (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "Implantação local ou em nuvem dedicada",
"pricing.plans.enterprise.feat4": "SLA 99,9 % garantido",
"pricing.plans.enterprise.feat5": "Suporte dedicado 24/7",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "IA multi-thread ultrarrápida",
"pricing.trust.availability.title": "Disponível 24/7",
"pricing.trust.availability.sub": "99,9 % de uptime garantido",
- "pricing.aiModels.title": "Nossos modelos de IA — Março 2026",
+ "pricing.aiModels.title": "Nossos modelos de IA — Setembro de 2026",
"pricing.aiModels.essential.title": "Tradução IA Essencial",
"pricing.aiModels.essential.plan": "Plano Pro",
"pricing.aiModels.essential.descPrefix": "Baseado em",
- "pricing.aiModels.essential.descSuffix": "— o modelo de IA mais econômico de 2026. Qualidade comparável a modelos frontier a uma fração do custo.",
- "pricing.aiModels.essential.modelName": "nosso modelo IA Essencial",
- "pricing.aiModels.essential.context": "163K tokens de contexto",
- "pricing.aiModels.essential.value": "Excelente custo-benefício",
+ "pricing.aiModels.essential.descSuffix": "— os modelos de IA com o melhor custo-benefício de 2026.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash e MiniMax M3",
+ "pricing.aiModels.essential.context": "Até 1,3 M de contexto (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "O melhor custo-benefício",
"pricing.aiModels.premium.title": "Tradução IA Premium",
"pricing.aiModels.premium.plan": "Plano Business",
"pricing.aiModels.premium.descPrefix": "Baseado em",
"pricing.aiModels.premium.descSuffix": "da Anthropic — preciso em documentos jurídicos, médicos e técnicos complexos.",
- "pricing.aiModels.premium.context": "200K tokens de contexto",
+ "pricing.aiModels.premium.context": "1M de contexto",
"pricing.aiModels.premium.precision": "Maior precisão",
"pricing.faq.title": "Perguntas frequentes",
"pricing.faq.q1": "Posso trocar de plano a qualquer momento?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "O que é a «Tradução IA Essencial»?",
"pricing.faq.a2": "É nosso motor de IA. Ele compreende o contexto dos seus documentos, preserva a formatação e lida com termos técnicos muito melhor do que a tradução clássica.",
"pricing.faq.q3": "Qual a diferença entre IA Essencial e IA Premium?",
- "pricing.faq.a3": "A IA Essencial usa um modelo otimizado (excelente custo-benefício). A IA Premium usa Claude 3.5 Haiku da Anthropic, mais precisa em documentos jurídicos, médicos e técnicos complexos.",
+ "pricing.faq.a3": "A IA Essencial usa DeepSeek V4 Flash, GLM-5.3 Flash e MiniMax M3 (excelente custo-benefício). A IA Premium usa Claude 5 da Anthropic, mais precisa em documentos jurídicos, médicos e técnicos complexos.",
"pricing.faq.q4": "Meus documentos são mantidos após a tradução?",
"pricing.faq.a4": "Os arquivos traduzidos ficam disponíveis de acordo com seu plano (30 dias Starter, 90 dias Pro, 1 ano Business). São criptografados em repouso e em trânsito.",
"pricing.faq.q5": "O que acontece se eu exceder minha cota mensal?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "Erro ao criar o pagamento.",
"pricing.dashboard": "Painel",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "A partir de 0,09 US$ por milhão de tokens",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Alternativas: DeepSeek V4 Pro e GLM-5.3",
+ "pricing.confirm.title": "Confirme a sua assinatura",
+ "pricing.confirm.subtitle": "Você será redirecionado ao nosso provedor de pagamento seguro.",
+ "pricing.confirm.cancel": "Cancelar",
+ "pricing.confirm.cta": "Continuar para o pagamento",
+ "pricing.confirm.year": "ano",
+ "pricing.confirm.month": "mês",
+ "pricing.confirm.monthlyEquivalent": "Faturação anual — ou seja, {price} € / mês.",
+ "pricing.confirm.secureNote": "Cancelável a qualquer momento no seu perfil. O pagamento é processado pelo Stripe; os dados do seu cartão nunca passam pelos nossos servidores.",
+ "pricing.enterprise.subject": "Pedido Enterprise",
+ "pricing.enterpriseBand.cta": "Fale conosco",
+ "pricing.enterpriseBand.text": "Volume, motores dedicados, opções on-premise — vamos conversar.",
+ "pricing.error.server": "Erro do servidor {status}",
+ "pricing.freeBand.cta": "Comece grátis",
+ "pricing.freeBand.text": "Só quer experimentar? Comece grátis — 5 documentos por mês, sem cartão.",
+ "pricing.header.titleBase": "Um plano para",
+ "pricing.header.titleAccent": "cada necessidade",
+ "pricing.toast.close": "Fechar",
+ "pricing.aiModels.premium.price": "2 US$ / 10 US$ por 1M de tokens"
}
diff --git a/frontend/src/lib/i18n/messages/pt/providerTheme.json b/frontend/src/lib/i18n/messages/pt/providerTheme.json
index b42e583..5da9815 100644
--- a/frontend/src/lib/i18n/messages/pt/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/pt/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Acesso unificado aos melhores modelos open-source otimizados para tradução.",
"providerTheme.openrouter_premium.badge": "Ultra",
"providerTheme.openrouter_premium.subBadge": "Contexto máximo",
- "providerTheme.openrouter_premium.desc": "Assistido pelos modelos mais avançados (GPT-4o, Claude Sonnet 4.6) para documentos longos.",
+ "providerTheme.openrouter_premium.desc": "Assistido pelos modelos mais avançados (GPT-4o, Claude 5) para documentos longos.",
"providerTheme.zai.badge": "Especializada",
"providerTheme.zai.subBadge": "Finanças e Direito",
"providerTheme.zai.desc": "Modelo afinado para terminologias empresariais exigentes (jurídico, financeiro).",
diff --git a/frontend/src/lib/i18n/messages/ru/admin.json b/frontend/src/lib/i18n/messages/ru/admin.json
index 5ea9122..39a1c51 100644
--- a/frontend/src/lib/i18n/messages/ru/admin.json
+++ b/frontend/src/lib/i18n/messages/ru/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "Провайдеры",
"admin.nav.system": "Система",
"admin.nav.logs": "Журналы",
+ "admin.nav.stats": "Статистика",
"admin.users.title": "Управление пользователями",
"admin.users.subtitle": "Просмотр и управление учётными записями",
"admin.users.planUpdated": "План обновлён",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "Ожидание данных...",
"admin.system.purging": "Очистка...",
"admin.system.clean": "Очистить",
- "admin.system.purge": "Очистить"
+ "admin.system.purge": "Очистить",
+ "admin.nav.models": "Модели и подписки",
+ "admin.nav.marketing": "Маркетинг",
+ "admin.marketing.audience": "Аудитория",
+ "admin.marketing.audienceAllUsers": "Все аккаунты",
+ "admin.marketing.audienceInactive": "Аккаунты без активности 30 дней",
+ "admin.marketing.audiencePlaceholder": "Выберите аудиторию",
+ "admin.marketing.audiencePlanBusiness": "Тариф Business",
+ "admin.marketing.audiencePlanEnterprise": "Тариф Enterprise",
+ "admin.marketing.audiencePlanFree": "Бесплатный тариф",
+ "admin.marketing.audiencePlanPro": "Тариф Pro",
+ "admin.marketing.audiencePlanStarter": "Тариф Starter",
+ "admin.marketing.audienceWaitlist": "Лист ожидания",
+ "admin.marketing.badgeReal": "реальная",
+ "admin.marketing.badgeTest": "тест",
+ "admin.marketing.confirmSend": "Отправить это письмо {count} получателю(ям) («{audience}»)?",
+ "admin.marketing.footerNote": "При отправке автоматически добавляется подвал со ссылкой отписки.",
+ "admin.marketing.hidePreview": "Скрыть предпросмотр",
+ "admin.marketing.history": "История рассылок",
+ "admin.marketing.historyFailedSuffix": ", {count} сбоев",
+ "admin.marketing.historySent": "{sent}/{total} отправлено",
+ "admin.marketing.html": "HTML-содержимое",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "Заполните тему и HTML.",
+ "admin.marketing.incompleteTitle": "Содержимое неполное",
+ "admin.marketing.loadErrorTitle": "Ошибка загрузки",
+ "admin.marketing.localCheckUnavailable": "Локальная проверка в этом браузере недоступна: сервер проверит предварительную тестовую отправку.",
+ "admin.marketing.networkDesc": "Не удаётся связаться с сервером.",
+ "admin.marketing.newCampaign": "Новая рассылка",
+ "admin.marketing.noHistory": "Рассылок не зарегистрировано.",
+ "admin.marketing.preview": "Предпросмотр",
+ "admin.marketing.previewEmpty": "Предпросмотр: вставьте HTML рядом с этой панелью.",
+ "admin.marketing.previewTitle": "Предпросмотр письма",
+ "admin.marketing.recipientsAvailable": "{count} получателей доступно после исключения отписавшихся.",
+ "admin.marketing.sendReal": "Отправить {count} получателю(ям)",
+ "admin.marketing.sendRefusedTitle": "Рассылка отклонена",
+ "admin.marketing.sendTest": "Тестовая отправка",
+ "admin.marketing.sendingDesc": "{count} письмо(ем) в очереди отправки (интервал 0,2 с). Отписавшиеся исключаются.",
+ "admin.marketing.sendingTitle": "Идёт рассылка",
+ "admin.marketing.subject": "Тема",
+ "admin.marketing.subjectPlaceholder": "Напр.: Ваш перевод готов — скидка 20% на 7 дней",
+ "admin.marketing.subtitle": "Рассылка писем по аудитории с обязательной тестовой отправкой, автоматической ссылкой отписки и полной историей.",
+ "admin.marketing.testDoneDesc": "Письмо получено на {email}",
+ "admin.marketing.testDoneTitle": "Тестовая отправка выполнена",
+ "admin.marketing.testEmail": "Тестовый email (необязательно)",
+ "admin.marketing.testEmailPlaceholder": "Иначе: SMTP-адрес отправителя",
+ "admin.marketing.testFailedTitle": "Тестовая отправка не удалась",
+ "admin.marketing.testRequiredHint": "Реальной рассылке предшествует тестовая отправка точно такого же содержимого (проверяется сервером).",
+ "admin.marketing.testValidated": "Тестовая отправка этого содержимого подтверждена.",
+ "admin.marketing.title": "Маркетинг — email-рассылки",
+ "admin.marketing.unsubscribedNote": "{count} отписавшихся будут исключены из любой рассылки.",
+ "admin.models.addModel": "Добавить модель",
+ "admin.models.businessBadge": "Тариф Business",
+ "admin.models.cancel": "Отмена",
+ "admin.models.catalog": "Каталог OpenRouter",
+ "admin.models.catalogErrorDesc": "Не удалось загрузить каталог OpenRouter.",
+ "admin.models.catalogErrorTitle": "Каталог недоступен",
+ "admin.models.customBadge": "Индивидуальный уровень",
+ "admin.models.customNote": "Специальные модели, согласовываются с клиентом индивидуально.",
+ "admin.models.defaultBadge": "по умолчанию",
+ "admin.models.emptyTier": "Нет моделей: будет использован официальный набор тарифа.",
+ "admin.models.enterpriseBadge": "Тариф Enterprise",
+ "admin.models.essentialBadge": "Уровень ИИ «Базовый»",
+ "admin.models.essentialCost": "— с коэффициентом стоимости 1",
+ "admin.models.fallbackFirst": "(запасная № 1)",
+ "admin.models.loadErrorDesc": "HTTP {status} — проверьте токен администратора.",
+ "admin.models.loadErrorTitle": "Ошибка загрузки",
+ "admin.models.matrixDesc": "Активные модели по уровням — порядок в списке задаёт приоритет запасного варианта. Радиокнопка выбирает модель по умолчанию, применяемую сразу, без повторного развёртывания.",
+ "admin.models.matrixTitle": "Матрица моделей по тарифам",
+ "admin.models.missingConfig": "Конфигурация уровней ИИ не найдена. Перезагрузите страницу.",
+ "admin.models.moveDown": "Вниз",
+ "admin.models.moveUp": "Вверх",
+ "admin.models.networkDesc": "Не удаётся связаться с сервером.",
+ "admin.models.networkTitle": "Сетевая ошибка",
+ "admin.models.premiumBadge": "Уровень ИИ «Премиум»",
+ "admin.models.premiumCost": "— с коэффициентом стоимости 5",
+ "admin.models.premiumReservedNote": "Только для тарифов Business и Enterprise (движок «openrouter_premium»).",
+ "admin.models.proBadge": "Тариф Pro",
+ "admin.models.remove": "Убрать",
+ "admin.models.save": "Сохранить",
+ "admin.models.saveErrorTitle": "Ошибка сохранения",
+ "admin.models.saveNetworkDesc": "Не удалось сохранить конфигурацию.",
+ "admin.models.savedDesc": "Новая модель по умолчанию применяется уже со следующего перевода, без повторного развёртывания.",
+ "admin.models.savedTitle": "Модели сохранены",
+ "admin.models.saving": "Сохранение...",
+ "admin.models.setDefault": "Назначить {model} моделью по умолчанию",
+ "admin.models.sharedTierNote": "Общий уровень: тариф Business также использует «Базовый» для движка «openrouter» — приведённый выше список относится к обоим тарифам.",
+ "admin.models.subtitle": "Матрица тарифы → уровни ИИ. Модель, реально используемая при переводе, — это модель уровня тарифа: тариф Pro никогда не задействует модель Premium.",
+ "admin.models.title": "Модели и подписки",
+ "admin.stats.aiTiers": "Уровни ИИ (30 дн.)",
+ "admin.stats.includingCredits": "в том числе {amount} кредитами",
+ "admin.stats.mrr": "Оценочный MRR",
+ "admin.stats.noTranslationsYet": "За последние 30 дней переводов в базе нет: распределение по уровням заполнится следующими переводами.",
+ "admin.stats.payments30": "{count} платёж(ей) за 30 дней",
+ "admin.stats.refreshing": "Обновление...",
+ "admin.stats.revenue30": "Выручка (30 дней)",
+ "admin.stats.revenueTotal": "Всего получено выручки",
+ "admin.stats.tiersSub": "Базовый / Премиум · классика {classic} · прочее {other}",
+ "admin.stats.unavailable": "Бизнес-статистика недоступна ({error}).",
+ "admin.stats.waitlist": "Лист ожидания",
+ "admin.stats.waitlistSub": "записей в листе ожидания"
}
diff --git a/frontend/src/lib/i18n/messages/ru/landing.json b/frontend/src/lib/i18n/messages/ru/landing.json
index f753d34..e5df422 100644
--- a/frontend/src/lib/i18n/messages/ru/landing.json
+++ b/frontend/src/lib/i18n/messages/ru/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "Для требовательных профессионалов",
"landing.pricing.pro.f1": "200 документов / мес.",
"landing.pricing.pro.f2": "До 200 страниц на документ",
- "landing.pricing.pro.f3": "Перевод на базе ИИ",
+ "landing.pricing.pro.f3": "Базовый ИИ: DeepSeek V4 Flash, GLM-5.3 Flash, MiniMax M3",
"landing.pricing.pro.f4": "Google включены",
"landing.pricing.pro.f5": "Пользовательские глоссарии и промпты",
"landing.pricing.pro.f6": "Приоритетная поддержка",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "Для команд с большими объёмами",
"landing.pricing.business.f1": "1 000 документов / мес.",
"landing.pricing.business.f2": "До 500 страниц на документ",
- "landing.pricing.business.f3": "Премиум ИИ (Claude)",
+ "landing.pricing.business.f3": "Премиум ИИ (Claude 5)",
"landing.pricing.business.f4": "Все провайдеры + API",
"landing.pricing.business.f5": "Вебхуки и автоматизация",
"landing.pricing.business.f6": "5 рабочих мест",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "Поддерживаются файлы DOCX, XLSX, PPTX или PDF",
"landing.translate.aiAnalysis": "Активный ИИ-анализ",
"landing.translate.processing": "Обработка",
- "landing.translate.preservingLayout": "Ваше форматирование сохраняется"
+ "landing.translate.preservingLayout": "Ваше форматирование сохраняется",
+ "landing.beforeAfter.seal": "Тот же макет, слово в слово",
+ "landing.beforeAfter.proof1": "Диаграммы SmartArt воссоздаются, а не сплющиваются",
+ "landing.beforeAfter.proof2": "Ряды и оси диаграмм переведены",
+ "landing.beforeAfter.proof3": "Оглавления заново формируются на целевом языке",
+ "landing.beforeAfter.targetTitle": "Техническая спецификация — Обработка воздуха",
+ "landing.beforeAfter.targetPara1": "Система вентиляции должна быть введена в эксплуатацию до конца второго квартала.",
+ "landing.beforeAfter.targetPara2Before": "",
+ "landing.beforeAfter.targetTerm": "Приточно-вытяжная установка",
+ "landing.beforeAfter.targetPara2After": "должна соответствовать классу фильтрации F7 согласно графику.",
+ "landing.beforeAfter.targetItem1": "Тепловая нагрузка: 42 кВт при номинальном расходе",
+ "landing.beforeAfter.targetItem2": "Уровень шума ниже 45 dB(A) на расстоянии 3 м",
+ "landing.formats.pill": "СОВМЕСТИМОСТЬ",
+ "landing.hero.visualCaption": "Тот же макет, новый язык — больше ничего не меняется",
+ "landing.pricing.free.name": "Бесплатно",
+ "landing.pricing.free.desc": "Идеально, чтобы попробовать приложение",
+ "landing.pricing.free.cta": "Выбрать этот план",
+ "landing.pricing.enterprise.name": "Enterprise",
+ "landing.pricing.enterprise.desc": "Индивидуальные решения для крупных организаций",
+ "landing.pricing.enterprise.cta": "Связаться с нами",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/ru/pricing.json b/frontend/src/lib/i18n/messages/ru/pricing.json
index bcd48a4..f76236e 100644
--- a/frontend/src/lib/i18n/messages/ru/pricing.json
+++ b/frontend/src/lib/i18n/messages/ru/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "История за 90 дней",
"pricing.plans.business.feat1": "1 000 документов / месяц",
"pricing.plans.business.feat2": "До 500 страниц на документ",
- "pricing.plans.business.feat3": "Базовый + Премиум ИИ (Claude Haiku)",
+ "pricing.plans.business.feat3": "Базовый + Премиум ИИ (Claude 5)",
"pricing.plans.business.feat4": "Все провайдеры перевода",
"pricing.plans.business.feat5": "Файлы до 50 МБ",
"pricing.plans.business.feat6": "Доступ к API (10 000 вызовов/мес.)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "История за 1 год",
"pricing.plans.business.feat10": "Расширенная аналитика",
"pricing.plans.enterprise.feat1": "Безлимитные документы",
- "pricing.plans.enterprise.feat2": "Все ИИ-модели (GPT-5, Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "Все ИИ-модели (Claude 5, DeepSeek V4 Pro, GLM-5.3…)",
"pricing.plans.enterprise.feat3": "Локальное развёртывание или выделенное облако",
"pricing.plans.enterprise.feat4": "SLA 99,9 % гарантировано",
"pricing.plans.enterprise.feat5": "Выделенная поддержка 24/7",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "Сверхбыстрая многопоточная ИИ",
"pricing.trust.availability.title": "Доступно 24/7",
"pricing.trust.availability.sub": "99,9 % гарантированный аптайм",
- "pricing.aiModels.title": "Наши ИИ-модели — март 2026",
+ "pricing.aiModels.title": "Наши ИИ-модели — сентябрь 2026",
"pricing.aiModels.essential.title": "Базовый ИИ-перевод",
"pricing.aiModels.essential.plan": "Тариф Pro",
"pricing.aiModels.essential.descPrefix": "На базе",
- "pricing.aiModels.essential.descSuffix": "— самой экономичной ИИ-модели 2026 года. Качество на уровне frontier-моделей при стоимости в десятки раз ниже.",
- "pricing.aiModels.essential.modelName": "наша базовая ИИ-модель",
- "pricing.aiModels.essential.context": "163K токенов контекста",
- "pricing.aiModels.essential.value": "Отличное соотношение цены и качества",
+ "pricing.aiModels.essential.descSuffix": "— самые экономичные ИИ-модели 2026 года.",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash, GLM-5.3 Flash и MiniMax M3",
+ "pricing.aiModels.essential.context": "Контекст до 1,3 млн токенов (GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "Лучшее соотношение цены и качества",
"pricing.aiModels.premium.title": "Премиум ИИ-перевод",
"pricing.aiModels.premium.plan": "Тариф Business",
"pricing.aiModels.premium.descPrefix": "На базе",
"pricing.aiModels.premium.descSuffix": "от Anthropic — высокая точность на юридических, медицинских и сложных технических документах.",
- "pricing.aiModels.premium.context": "200K токенов контекста",
+ "pricing.aiModels.premium.context": "1 млн токенов контекста",
"pricing.aiModels.premium.precision": "Наивысшая точность",
"pricing.faq.title": "Часто задаваемые вопросы",
"pricing.faq.q1": "Могу ли я сменить тариф в любое время?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "Что такое «Базовый ИИ-перевод»?",
"pricing.faq.a2": "Это наш ИИ-движок. Он понимает контекст ваших документов, сохраняет вёрстку и обрабатывает технические термины намного лучше классического перевода.",
"pricing.faq.q3": "В чём разница между базовым и премиум ИИ-переводом?",
- "pricing.faq.a3": "Базовый ИИ использует оптимизированную модель (отличное соотношение цены и качества). Премиум ИИ использует Claude 3.5 Haiku от Anthropic, более точный на юридических, медицинских и сложных технических документах.",
+ "pricing.faq.a3": "Базовый ИИ работает на моделях DeepSeek V4 Flash, GLM-5.3 Flash и MiniMax M3 (отличное соотношение цены и качества). Премиум ИИ использует Claude 5 от Anthropic, более точный на юридических, медицинских и сложных технических документах.",
"pricing.faq.q4": "Сохраняются ли мои документы после перевода?",
"pricing.faq.a4": "Переведённые файлы доступны в зависимости от тарифа (30 дней Starter, 90 дней Pro, 1 год Business). Они зашифрованы при хранении и передаче.",
"pricing.faq.q5": "Что произойдёт при превышении месячной квоты?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "Ошибка при создании платежа.",
"pricing.dashboard": "Панель",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "От $0,09 за 1 млн токенов",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "Альтернативы: DeepSeek V4 Pro и GLM-5.3",
+ "pricing.confirm.title": "Подтвердите подписку",
+ "pricing.confirm.subtitle": "Вы будете перенаправлены к нашему безопасному платёжному провайдеру.",
+ "pricing.confirm.cancel": "Отмена",
+ "pricing.confirm.cta": "Перейти к оплате",
+ "pricing.confirm.year": "год",
+ "pricing.confirm.month": "месяц",
+ "pricing.confirm.monthlyEquivalent": "Годовая оплата — то есть {price} € / месяц.",
+ "pricing.confirm.secureNote": "Отмена в любой момент из профиля. Платёж обрабатывает Stripe; данные вашей карты никогда не попадают на наши серверы.",
+ "pricing.enterprise.subject": "Запрос по тарифу Enterprise",
+ "pricing.enterpriseBand.cta": "Связаться с нами",
+ "pricing.enterpriseBand.text": "Объёмы, выделенные движки, локальное размещение — давайте обсудим.",
+ "pricing.error.server": "Ошибка сервера {status}",
+ "pricing.freeBand.cta": "Начать бесплатно",
+ "pricing.freeBand.text": "Хотите просто попробовать? Начните бесплатно — 5 документов в месяц, без карты.",
+ "pricing.header.titleBase": "Тариф для",
+ "pricing.header.titleAccent": "каждой задачи",
+ "pricing.toast.close": "Закрыть",
+ "pricing.aiModels.premium.price": "$2 / $10 за 1 млн токенов"
}
diff --git a/frontend/src/lib/i18n/messages/ru/providerTheme.json b/frontend/src/lib/i18n/messages/ru/providerTheme.json
index 9ea89b1..5a303e7 100644
--- a/frontend/src/lib/i18n/messages/ru/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/ru/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "Единый доступ к лучшим open-source моделям, оптимизированным для перевода.",
"providerTheme.openrouter_premium.badge": "Ультра",
"providerTheme.openrouter_premium.subBadge": "Максимальный контекст",
- "providerTheme.openrouter_premium.desc": "С помощью современных моделей (GPT-4o, Claude Sonnet 4.6) для длинных документов.",
+ "providerTheme.openrouter_premium.desc": "С помощью современных моделей (GPT-4o, Claude 5) для длинных документов.",
"providerTheme.zai.badge": "Специализированный",
"providerTheme.zai.subBadge": "Финансы и право",
"providerTheme.zai.desc": "Модель точно настроена для требовательных бизнес-терминологий (юриспруденция, финансы).",
diff --git a/frontend/src/lib/i18n/messages/zh/admin.json b/frontend/src/lib/i18n/messages/zh/admin.json
index ffcb049..289cbba 100644
--- a/frontend/src/lib/i18n/messages/zh/admin.json
+++ b/frontend/src/lib/i18n/messages/zh/admin.json
@@ -20,6 +20,7 @@
"admin.nav.providers": "服务商",
"admin.nav.system": "系统",
"admin.nav.logs": "日志",
+ "admin.nav.stats": "统计",
"admin.users.title": "用户管理",
"admin.users.subtitle": "查看和管理用户账户",
"admin.users.planUpdated": "套餐已更新",
@@ -44,5 +45,105 @@
"admin.system.waitingData": "等待数据...",
"admin.system.purging": "清理中...",
"admin.system.clean": "清理",
- "admin.system.purge": "清除"
+ "admin.system.purge": "清除",
+ "admin.nav.models": "模型与订阅",
+ "admin.nav.marketing": "市场营销",
+ "admin.marketing.audience": "目标人群",
+ "admin.marketing.audienceAllUsers": "全部账户",
+ "admin.marketing.audienceInactive": "30 天未活动的账户",
+ "admin.marketing.audiencePlaceholder": "选择目标人群",
+ "admin.marketing.audiencePlanBusiness": "Business 套餐",
+ "admin.marketing.audiencePlanEnterprise": "Enterprise 套餐",
+ "admin.marketing.audiencePlanFree": "免费套餐",
+ "admin.marketing.audiencePlanPro": "Pro 套餐",
+ "admin.marketing.audiencePlanStarter": "Starter 套餐",
+ "admin.marketing.audienceWaitlist": "等候名单",
+ "admin.marketing.badgeReal": "正式",
+ "admin.marketing.badgeTest": "测试",
+ "admin.marketing.confirmSend": "将此邮件发送给 {count} 位收件人(「{audience}」)?",
+ "admin.marketing.footerNote": "发送时会自动添加包含退订链接的页脚。",
+ "admin.marketing.hidePreview": "隐藏预览",
+ "admin.marketing.history": "发送历史",
+ "admin.marketing.historyFailedSuffix": ",{count} 封失败",
+ "admin.marketing.historySent": "已发送 {sent}/{total}",
+ "admin.marketing.html": "HTML 内容",
+ "admin.marketing.htmlPlaceholder": "…",
+ "admin.marketing.incompleteDesc": "请填写主题和 HTML。",
+ "admin.marketing.incompleteTitle": "内容不完整",
+ "admin.marketing.loadErrorTitle": "加载错误",
+ "admin.marketing.localCheckUnavailable": "此浏览器不支持本地校验:服务器将检查事先的测试发送。",
+ "admin.marketing.networkDesc": "无法连接后端。",
+ "admin.marketing.newCampaign": "新营销活动",
+ "admin.marketing.noHistory": "暂无发送记录。",
+ "admin.marketing.preview": "预览",
+ "admin.marketing.previewEmpty": "预览:请在旁边粘贴您的 HTML。",
+ "admin.marketing.previewTitle": "邮件预览",
+ "admin.marketing.recipientsAvailable": "排除退订用户后,共有 {count} 位收件人。",
+ "admin.marketing.sendReal": "发送给 {count} 位收件人",
+ "admin.marketing.sendRefusedTitle": "发送被拒绝",
+ "admin.marketing.sendTest": "测试发送",
+ "admin.marketing.sendingDesc": "{count} 封邮件进入发送队列(间隔 0.2 秒)。退订用户已被排除。",
+ "admin.marketing.sendingTitle": "发送进行中",
+ "admin.marketing.subject": "主题",
+ "admin.marketing.subjectPlaceholder": "例如:您的翻译已完成 — 7 天内 8 折",
+ "admin.marketing.subtitle": "向目标人群发送邮件,必须先测试发送,自动附加退订链接,并保留完整历史。",
+ "admin.marketing.testDoneDesc": "邮件已送达 {email}",
+ "admin.marketing.testDoneTitle": "测试发送完成",
+ "admin.marketing.testEmail": "测试邮箱(可选)",
+ "admin.marketing.testEmailPlaceholder": "否则使用 SMTP 发件地址",
+ "admin.marketing.testFailedTitle": "测试发送失败",
+ "admin.marketing.testRequiredHint": "正式发送要求先对完全相同的内容进行测试发送(由服务器校验)。",
+ "admin.marketing.testValidated": "此内容的测试发送已通过。",
+ "admin.marketing.title": "营销 — 邮件营销",
+ "admin.marketing.unsubscribedNote": "{count} 位退订用户将被排除在所有发送之外。",
+ "admin.models.addModel": "添加模型",
+ "admin.models.businessBadge": "Business 套餐",
+ "admin.models.cancel": "取消",
+ "admin.models.catalog": "OpenRouter 目录",
+ "admin.models.catalogErrorDesc": "无法加载 OpenRouter 目录。",
+ "admin.models.catalogErrorTitle": "目录不可用",
+ "admin.models.customBadge": "定制层级",
+ "admin.models.customNote": "定制模型,与客户逐案商定。",
+ "admin.models.defaultBadge": "默认",
+ "admin.models.emptyTier": "暂无模型:将使用套餐的官方模型阵容。",
+ "admin.models.enterpriseBadge": "Enterprise 套餐",
+ "admin.models.essentialBadge": "基础 AI 层级",
+ "admin.models.essentialCost": "— 按成本系数 1 计费",
+ "admin.models.fallbackFirst": "(首选备用)",
+ "admin.models.loadErrorDesc": "HTTP {status} — 请检查管理员令牌。",
+ "admin.models.loadErrorTitle": "加载错误",
+ "admin.models.matrixDesc": "每个层级的有效模型 — 列表顺序即备用优先级。单选按钮选择默认模型,立即生效,无需重新部署。",
+ "admin.models.matrixTitle": "按套餐划分的模型矩阵",
+ "admin.models.missingConfig": "未找到 AI 层级配置。请刷新页面。",
+ "admin.models.moveDown": "下移",
+ "admin.models.moveUp": "上移",
+ "admin.models.networkDesc": "无法连接后端。",
+ "admin.models.networkTitle": "网络错误",
+ "admin.models.premiumBadge": "高级 AI 层级",
+ "admin.models.premiumCost": "— 按成本系数 5 计费",
+ "admin.models.premiumReservedNote": "仅限 Business 和 Enterprise 套餐(「openrouter_premium」引擎)。",
+ "admin.models.proBadge": "Pro 套餐",
+ "admin.models.remove": "移除",
+ "admin.models.save": "保存",
+ "admin.models.saveErrorTitle": "保存错误",
+ "admin.models.saveNetworkDesc": "无法保存配置。",
+ "admin.models.savedDesc": "新的默认模型从下一次翻译起生效,无需重新部署。",
+ "admin.models.savedTitle": "模型已保存",
+ "admin.models.saving": "保存中...",
+ "admin.models.setDefault": "将 {model} 设为默认模型",
+ "admin.models.sharedTierNote": "共享层级:Business 套餐的「openrouter」引擎同样使用基础层级 — 上面的列表对两个套餐都适用。",
+ "admin.models.subtitle": "套餐 → AI 层级矩阵。每次翻译实际使用的模型是套餐所属层级的模型:Pro 套餐绝不会触发 Premium 模型。",
+ "admin.models.title": "模型与订阅",
+ "admin.stats.aiTiers": "AI 层级(30 天)",
+ "admin.stats.includingCredits": "其中 {amount} 为积分",
+ "admin.stats.mrr": "预估 MRR",
+ "admin.stats.noTranslationsYet": "最近 30 天数据库中没有翻译记录:层级分布将随后续翻译逐步填充。",
+ "admin.stats.payments30": "30 天内 {count} 笔付款",
+ "admin.stats.refreshing": "刷新中...",
+ "admin.stats.revenue30": "收入(30 天)",
+ "admin.stats.revenueTotal": "累计已收收入",
+ "admin.stats.tiersSub": "基础 / 高级 · 经典 {classic} · 其他 {other}",
+ "admin.stats.unavailable": "业务统计不可用({error})。",
+ "admin.stats.waitlist": "等候名单",
+ "admin.stats.waitlistSub": "位用户等候中"
}
diff --git a/frontend/src/lib/i18n/messages/zh/landing.json b/frontend/src/lib/i18n/messages/zh/landing.json
index e716fb9..736bd40 100644
--- a/frontend/src/lib/i18n/messages/zh/landing.json
+++ b/frontend/src/lib/i18n/messages/zh/landing.json
@@ -100,7 +100,7 @@
"landing.pricing.pro.desc": "适合专业用户",
"landing.pricing.pro.f1": "每月200份文档",
"landing.pricing.pro.f2": "每份文档最多200页",
- "landing.pricing.pro.f3": "AI翻译",
+ "landing.pricing.pro.f3": "基础 AI:DeepSeek V4 Flash、GLM-5.3 Flash、MiniMax M3",
"landing.pricing.pro.f4": "包含Google",
"landing.pricing.pro.f5": "自定义术语表和提示词",
"landing.pricing.pro.f6": "优先支持",
@@ -109,7 +109,7 @@
"landing.pricing.business.desc": "适合大批量需求的团队",
"landing.pricing.business.f1": "每月1,000份文档",
"landing.pricing.business.f2": "每份文档最多500页",
- "landing.pricing.business.f3": "高级AI(Claude)",
+ "landing.pricing.business.f3": "高级AI(Claude 5)",
"landing.pricing.business.f4": "所有供应商 + API",
"landing.pricing.business.f5": "Webhook和自动化",
"landing.pricing.business.f6": "5个团队席位",
@@ -142,5 +142,31 @@
"landing.translate.supportedFormats": "支持 DOCX, XLSX, PPTX 或 PDF 文件",
"landing.translate.aiAnalysis": "AI 分析中",
"landing.translate.processing": "正在处理",
- "landing.translate.preservingLayout": "正在保留您的排版"
+ "landing.translate.preservingLayout": "正在保留您的排版",
+ "landing.beforeAfter.seal": "相同的排版,逐字对应",
+ "landing.beforeAfter.proof1": "SmartArt 图形会重新构建,而不是被压平",
+ "landing.beforeAfter.proof2": "图表系列与坐标轴一并翻译",
+ "landing.beforeAfter.proof3": "目录按目标语言重新生成",
+ "landing.beforeAfter.targetTitle": "技术规格 — 空气处理",
+ "landing.beforeAfter.targetPara1": "通风系统必须在第二季度结束前调试运行。",
+ "landing.beforeAfter.targetPara2Before": "",
+ "landing.beforeAfter.targetTerm": "空气处理机组",
+ "landing.beforeAfter.targetPara2After": "必须按照计划达到 F7 过滤等级。",
+ "landing.beforeAfter.targetItem1": "热负荷:额定流量下 42 kW",
+ "landing.beforeAfter.targetItem2": "3 米处噪音低于 45 dB(A)",
+ "landing.formats.pill": "兼容性",
+ "landing.hero.visualCaption": "相同的排版,新的语言 — 其余一切保持不变",
+ "landing.pricing.free.name": "免费",
+ "landing.pricing.free.desc": "适合初次体验应用",
+ "landing.pricing.free.cta": "选择此套餐",
+ "landing.pricing.enterprise.name": "企业版",
+ "landing.pricing.enterprise.desc": "面向大型组织的定制解决方案",
+ "landing.pricing.enterprise.cta": "联系我们",
+ "landing.beforeAfter.sourceTitle": "Cahier des charges — Traitement d'air",
+ "landing.beforeAfter.sourcePara1": "L'installation de ventilation doit être mise en service avant la fin du deuxième trimestre.",
+ "landing.beforeAfter.sourcePara2Before": "Le",
+ "landing.beforeAfter.sourceTerm": "groupe de traitement d'air",
+ "landing.beforeAfter.sourcePara2After": "doit respecter la classe de filtration F7 conformément au planning.",
+ "landing.beforeAfter.sourceItem1": "Charge thermique : 42 kW au débit nominal",
+ "landing.beforeAfter.sourceItem2": "Niveau sonore inférieur à 45 dB(A) à 3 m"
}
diff --git a/frontend/src/lib/i18n/messages/zh/pricing.json b/frontend/src/lib/i18n/messages/zh/pricing.json
index 8337afb..ec2c777 100644
--- a/frontend/src/lib/i18n/messages/zh/pricing.json
+++ b/frontend/src/lib/i18n/messages/zh/pricing.json
@@ -41,7 +41,7 @@
"pricing.plans.pro.feat8": "90 天历史记录",
"pricing.plans.business.feat1": "每月 1,000 份文档",
"pricing.plans.business.feat2": "每份文档最多 500 页",
- "pricing.plans.business.feat3": "基础 + 高级 AI(Claude Haiku)",
+ "pricing.plans.business.feat3": "基础 + 高级 AI(Claude 5)",
"pricing.plans.business.feat4": "所有翻译服务商",
"pricing.plans.business.feat5": "文件最大 50 MB",
"pricing.plans.business.feat6": "API 访问(每月 10,000 次调用)",
@@ -50,7 +50,7 @@
"pricing.plans.business.feat9": "1 年历史记录",
"pricing.plans.business.feat10": "高级分析",
"pricing.plans.enterprise.feat1": "无限文档",
- "pricing.plans.enterprise.feat2": "所有 AI 模型(GPT-5、Claude Opus 4.6…)",
+ "pricing.plans.enterprise.feat2": "所有 AI 模型(Claude 5、DeepSeek V4 Pro、GLM-5.3…)",
"pricing.plans.enterprise.feat3": "本地部署或专属云",
"pricing.plans.enterprise.feat4": "99.9% SLA 保障",
"pricing.plans.enterprise.feat5": "24/7 专属支持",
@@ -108,19 +108,19 @@
"pricing.trust.parallel.sub": "超快多线程 AI",
"pricing.trust.availability.title": "7×24 小时可用",
"pricing.trust.availability.sub": "99.9% 可用性保障",
- "pricing.aiModels.title": "我们的 AI 模型 — 2026年3月",
+ "pricing.aiModels.title": "我们的 AI 模型 — 2026年9月",
"pricing.aiModels.essential.title": "基础 AI 翻译",
"pricing.aiModels.essential.plan": "专业版方案",
"pricing.aiModels.essential.descPrefix": "基于",
- "pricing.aiModels.essential.descSuffix": "— 2026 年最具性价比的 AI 模型。质量媲美前沿模型,成本仅为其一小部分。",
- "pricing.aiModels.essential.modelName": "Essential AI模型",
- "pricing.aiModels.essential.context": "163K 上下文 Token",
- "pricing.aiModels.essential.value": "性价比极佳",
+ "pricing.aiModels.essential.descSuffix": "— 2026 年最具性价比的 AI 模型。",
+ "pricing.aiModels.essential.modelName": "DeepSeek V4 Flash、GLM-5.3 Flash 和 MiniMax M3",
+ "pricing.aiModels.essential.context": "上下文最多 130 万 Token(GLM-5.3 Flash)",
+ "pricing.aiModels.essential.value": "极致性价比",
"pricing.aiModels.premium.title": "高级 AI 翻译",
"pricing.aiModels.premium.plan": "企业版方案",
"pricing.aiModels.premium.descPrefix": "基于",
"pricing.aiModels.premium.descSuffix": "Anthropic 出品 — 在法律、医疗和复杂技术文档方面表现精准。",
- "pricing.aiModels.premium.context": "200K 上下文 Token",
+ "pricing.aiModels.premium.context": "100 万上下文 Token",
"pricing.aiModels.premium.precision": "最高精度",
"pricing.faq.title": "常见问题",
"pricing.faq.q1": "我可以随时更换方案吗?",
@@ -128,7 +128,7 @@
"pricing.faq.q2": "什么是「基础 AI 翻译」?",
"pricing.faq.a2": "这是我们的AI引擎。它能理解文档上下文,保留排版,并比传统翻译更好地处理技术术语。",
"pricing.faq.q3": "基础 AI 和高级 AI 有什么区别?",
- "pricing.faq.a3": "Essential AI使用优化模型(性价比极佳)。Premium AI使用Anthropic的Claude 3.5 Haiku,在法律、医学和复杂技术文档上更准确。",
+ "pricing.faq.a3": "Essential AI使用DeepSeek V4 Flash、GLM-5.3 Flash和MiniMax M3(性价比极佳)。Premium AI使用Anthropic的Claude 5,在法律、医学和复杂技术文档上更准确。",
"pricing.faq.q4": "翻译后文档会被保留吗?",
"pricing.faq.a4": "翻译文件根据您的方案保留(入门版 30 天、专业版 90 天、企业版 1 年)。存储和传输过程中均已加密。",
"pricing.faq.q5": "超过月度配额会怎样?",
@@ -146,5 +146,26 @@
"pricing.toast.paymentError": "创建支付时出错。",
"pricing.dashboard": "仪表板",
"pricing.okSymbol": "✓",
- "pricing.errSymbol": "✕"
+ "pricing.errSymbol": "✕",
+ "pricing.aiModels.essential.price": "每百万 Token 低至 $0.09",
+ "pricing.aiModels.premium.modelName": "Claude 5",
+ "pricing.aiModels.premium.alternatives": "备选模型:DeepSeek V4 Pro 和 GLM-5.3",
+ "pricing.confirm.title": "确认您的订阅",
+ "pricing.confirm.subtitle": "您将被重定向到我们的安全支付服务商。",
+ "pricing.confirm.cancel": "取消",
+ "pricing.confirm.cta": "继续付款",
+ "pricing.confirm.year": "年",
+ "pricing.confirm.month": "个月",
+ "pricing.confirm.monthlyEquivalent": "按年计费 — 相当于每月 {price} 欧元。",
+ "pricing.confirm.secureNote": "可随时在个人资料中取消。付款由 Stripe 处理;您的银行卡信息绝不会经过我们的服务器。",
+ "pricing.enterprise.subject": "企业版方案咨询",
+ "pricing.enterpriseBand.cta": "联系我们",
+ "pricing.enterpriseBand.text": "大批量、专属引擎、本地部署选项 — 欢迎洽谈。",
+ "pricing.error.server": "服务器错误 {status}",
+ "pricing.freeBand.cta": "免费开始",
+ "pricing.freeBand.text": "只想先试试?免费开始 — 每月 5 份文档,无需银行卡。",
+ "pricing.header.titleBase": "总有一款套餐适合",
+ "pricing.header.titleAccent": "您的需求",
+ "pricing.toast.close": "关闭",
+ "pricing.aiModels.premium.price": "每百万 Token $2 / $10"
}
diff --git a/frontend/src/lib/i18n/messages/zh/providerTheme.json b/frontend/src/lib/i18n/messages/zh/providerTheme.json
index 90160c1..78f1b26 100644
--- a/frontend/src/lib/i18n/messages/zh/providerTheme.json
+++ b/frontend/src/lib/i18n/messages/zh/providerTheme.json
@@ -13,7 +13,7 @@
"providerTheme.openrouter.desc": "统一访问为翻译优化的最佳开源模型。",
"providerTheme.openrouter_premium.badge": "超级",
"providerTheme.openrouter_premium.subBadge": "最大上下文",
- "providerTheme.openrouter_premium.desc": "由尖端模型(GPT-4o、Claude Sonnet 4.6)辅助,适用于长文档。",
+ "providerTheme.openrouter_premium.desc": "由尖端模型(GPT-4o、Claude 5)辅助,适用于长文档。",
"providerTheme.zai.badge": "专业",
"providerTheme.zai.subBadge": "金融与法律",
"providerTheme.zai.desc": "针对苛刻的业务术语(法律、金融)进行了微调的模型。",
diff --git a/frontend/src/lib/store.ts b/frontend/src/lib/store.ts
index bf5700e..3ce0959 100644
--- a/frontend/src/lib/store.ts
+++ b/frontend/src/lib/store.ts
@@ -8,9 +8,12 @@ export const openaiModels = [
];
export const openrouterModels = [
- { id: "google/gemini-3.5-flash", name: "Gemini 3.5 Flash" },
- { id: "deepseek/deepseek-chat", name: "DeepSeek Chat" },
- { id: "anthropic/claude-sonnet-4.6", name: "Claude Sonnet 4.6" },
+ { id: "deepseek/deepseek-v4-flash", name: "DeepSeek V4 Flash" },
+ { id: "z-ai/glm-5.3-flash", name: "GLM-5.3 Flash" },
+ { id: "minimax/minimax-m3", name: "MiniMax M3" },
+ { id: "anthropic/claude-sonnet-5", name: "Claude 5" },
+ { id: "deepseek/deepseek-v4-pro", name: "DeepSeek V4 Pro" },
+ { id: "z-ai/glm-5.3", name: "GLM-5.3" },
];
interface TranslationSettings {
@@ -101,7 +104,7 @@ const defaultSettings: TranslationSettings = {
openaiApiKey: "",
openaiModel: "gpt-4o-mini",
openrouterApiKey: "",
- openrouterModel: "deepseek/deepseek-chat-v3-0324",
+ openrouterModel: "deepseek/deepseek-v4-flash",
libreTranslateUrl: "",
systemPrompt: "",
glossary: "",
diff --git a/models/subscription.py b/models/subscription.py
index 26ccac4..1bd61ca 100644
--- a/models/subscription.py
+++ b/models/subscription.py
@@ -31,10 +31,24 @@ import os
# NOTE: Stripe price IDs must be set via env vars in production.
# Create products/prices in Stripe Dashboard: https://dashboard.stripe.com/products
#
-# LLM models used (via OpenRouter — June 2026):
-# Essentielle : deepseek/deepseek-chat (DeepSeek V4/V3)
-# Premium : anthropic/claude-sonnet-4.6 (Claude Sonnet 4.6)
-# or google/gemini-3.5-flash (Gemini 3.5 Flash)
+# Official LLM ranges (via OpenRouter — September 2026). The model used per
+# translation is resolved from the plan's AI tier (admin settings > plan default):
+# Essentielle (Pro plan) : deepseek/deepseek-v4-flash, z-ai/glm-5.3-flash,
+# minimax/minimax-m3
+# Premium (Business) : anthropic/claude-sonnet-5 (default),
+# alternatives deepseek/deepseek-v4-pro, z-ai/glm-5.3
+# A Pro user must never trigger a Premium model (see translate_routes).
+DEFAULT_AI_MODELS_ESSENTIAL = [
+ "deepseek/deepseek-v4-flash",
+ "z-ai/glm-5.3-flash",
+ "minimax/minimax-m3",
+]
+DEFAULT_AI_MODELS_PREMIUM = [
+ "anthropic/claude-sonnet-5",
+ "deepseek/deepseek-v4-pro",
+ "z-ai/glm-5.3",
+]
+
PLANS = {
PlanType.FREE: {
"name": "Free",
@@ -97,7 +111,7 @@ PLANS = {
"max_file_size_mb": 25,
"max_chars_per_month": 2_000_000,
"providers": ["google", "google_cloud", "openrouter"],
- "ai_model_essential": "deepseek/deepseek-chat",
+ "ai_models_essential": list(DEFAULT_AI_MODELS_ESSENTIAL),
"features": [
"pricing.plans.pro.feat1",
"pricing.plans.pro.feat2",
@@ -127,8 +141,8 @@ PLANS = {
"max_file_size_mb": 50,
"max_chars_per_month": 10_000_000,
"providers": ["google", "google_cloud", "openrouter", "openrouter_premium", "openai", "zai"],
- "ai_model_essential": "deepseek/deepseek-chat",
- "ai_model_premium": "anthropic/claude-sonnet-4.6",
+ "ai_models_essential": list(DEFAULT_AI_MODELS_ESSENTIAL),
+ "ai_models_premium": list(DEFAULT_AI_MODELS_PREMIUM),
"features": [
"pricing.plans.business.feat1",
"pricing.plans.business.feat2",
diff --git a/routes/admin_routes.py b/routes/admin_routes.py
index fe11781..4fbab74 100644
--- a/routes/admin_routes.py
+++ b/routes/admin_routes.py
@@ -5,19 +5,31 @@ Story 3.5: API Versioning - Migrated from main.py
"""
import os
+import json
+import re
+import asyncio
+import html as html_module
import secrets
import time
import logging
-from datetime import datetime, timezone
-from typing import Optional, Literal
+import hashlib
+import threading
+import uuid
+from datetime import datetime, timedelta, timezone
+from typing import Optional, Literal, List
from fastapi import APIRouter, Depends, Header, HTTPException, Form, Request, Query
-from fastapi.responses import JSONResponse
+from fastapi.responses import JSONResponse, HTMLResponse
from passlib.context import CryptContext
from pydantic import BaseModel, Field
from config import config
-from models.subscription import PlanType, PLANS
+from models.subscription import (
+ PlanType,
+ PLANS,
+ DEFAULT_AI_MODELS_ESSENTIAL,
+ DEFAULT_AI_MODELS_PREMIUM,
+)
from services import pricing_config as pricing_cfg
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -619,7 +631,122 @@ async def get_admin_stats(admin_id: str = Depends(require_admin)):
if docs > 0:
active_users += 1
- cache_stats = _translation_cache.get_stats()
+ # Correction : TranslationCache expose stats() (get_stats n'a jamais existé —
+ # l'appel cassé renvoyait une erreur 500 sur GET /stats avant cette correction).
+ try:
+ cache_stats = _translation_cache.stats()
+ except Exception as e:
+ logger.warning("Cache stats unavailable: %s", e)
+ cache_stats = {}
+
+ # ------------------------------------------------------------------
+ # Statistiques enrichies (spec « Modèles & abonnements ») : revenus,
+ # MRR estimé, crédits achetés, liste d'attente, paliers IA utilisés.
+ # Toutes ces métriques sont best-effort : un échec ne casse pas /stats.
+ # ------------------------------------------------------------------
+ revenue = {
+ "collected_total": 0.0,
+ "collected_30d": 0.0,
+ "credits_purchased": 0.0,
+ "payments_30d": 0,
+ "currency": "EUR",
+ }
+ mrr_estimated = {"total": 0.0, "by_plan": {}}
+ ai_tier_usage = {"essential": 0, "premium": 0, "classic": 0, "other": 0}
+ waitlist_count = 0
+
+ # MRR estimé : pour chaque plan payé, abonnés × prix mensuel effectif
+ # (overrides admin inclus via get_effective_monthly_yearly).
+ for plan_value, count in plan_distribution.items():
+ try:
+ monthly, _yearly = pricing_cfg.get_effective_monthly_yearly(plan_value)
+ except Exception:
+ continue
+ if monthly and monthly > 0:
+ mrr_plan = round(monthly * count, 2)
+ mrr_estimated["by_plan"][plan_value] = mrr_plan
+ mrr_estimated["total"] = round(mrr_estimated["total"] + mrr_plan, 2)
+
+ try:
+ from routes.waitlist_routes import _load_waitlist
+
+ waitlist_count = len(_load_waitlist())
+ except Exception as e:
+ logger.debug("Waitlist count unavailable: %s", e)
+
+ if USE_DATABASE and DATABASE_AVAILABLE:
+ try:
+ from database.connection import get_sync_session
+ from database.models import PaymentHistory as DBPayment
+ from database.models import Translation as DBTranslation
+ from sqlalchemy import func as _sqlfunc
+
+ cutoff_30d = datetime.now(timezone.utc) - timedelta(days=30)
+ with get_sync_session() as session:
+ # Devise dominante (le site facture en EUR ; Stripe peut renvoyer
+ # une autre devise pour d'anciens paiements) — agrégation en SQL.
+ currency_totals = (
+ session.query(
+ DBPayment.currency,
+ _sqlfunc.sum(DBPayment.amount_cents),
+ )
+ .filter(DBPayment.status == "succeeded")
+ .group_by(DBPayment.currency)
+ .all()
+ )
+ if currency_totals:
+ main_currency, _main_total = max(
+ currency_totals, key=lambda row: (row[1] or 0)
+ )
+
+ def _sum_cents(extra_filters=()):
+ q = session.query(_sqlfunc.sum(DBPayment.amount_cents)).filter(
+ DBPayment.status == "succeeded",
+ DBPayment.currency == main_currency,
+ )
+ for f in extra_filters:
+ q = q.filter(f)
+ return (q.scalar() or 0) / 100.0
+
+ def _count_payments(extra_filters=()):
+ q = session.query(_sqlfunc.count(DBPayment.id)).filter(
+ DBPayment.status == "succeeded",
+ DBPayment.currency == main_currency,
+ )
+ for f in extra_filters:
+ q = q.filter(f)
+ return q.scalar() or 0
+
+ total = _sum_cents()
+ last30 = _sum_cents((DBPayment.created_at >= cutoff_30d,))
+ credits = _sum_cents((DBPayment.payment_type == "credits",))
+ n30 = _count_payments((DBPayment.created_at >= cutoff_30d,))
+ revenue.update(
+ collected_total=round(total, 2),
+ collected_30d=round(last30, 2),
+ credits_purchased=round(credits, 2),
+ payments_30d=n30,
+ currency=(main_currency or "eur").upper(),
+ )
+
+ # Répartition des paliers IA utilisés (traductions des 30 derniers jours).
+ tier_rows = (
+ session.query(DBTranslation.provider)
+ .filter(DBTranslation.created_at >= cutoff_30d)
+ .all()
+ )
+ for (prov,) in tier_rows:
+ p = (prov or "").strip().lower()
+ if p == "openrouter_premium":
+ ai_tier_usage["premium"] += 1
+ elif p in ("openrouter", "deepseek", "minimax", "zai"):
+ ai_tier_usage["essential"] += 1
+ elif p in ("google", "google_cloud"):
+ ai_tier_usage["classic"] += 1
+ else:
+ ai_tier_usage["other"] += 1
+ except Exception as e:
+ logger.warning("Revenue/tier stats unavailable: %s", e)
return {
"users": {
@@ -631,6 +758,10 @@ async def get_admin_stats(admin_id: str = Depends(require_admin)):
"docs_this_month": total_docs_translated,
"pages_this_month": total_pages_translated,
},
+ "revenue": revenue,
+ "mrr_estimated": mrr_estimated,
+ "ai_tier_usage": ai_tier_usage,
+ "waitlist_count": waitlist_count,
"cache": cache_stats,
"config": {
"translation_service": config.TRANSLATION_SERVICE,
@@ -911,6 +1042,32 @@ class SmtpSettings(BaseModel):
use_tls: bool = True
+class AiTierSettings(BaseModel):
+ """Palier IA : modèles actifs (l'ordre = priorité de secours) + modèle par défaut.
+
+ ``default_model`` vide → premier de ``models`` → défaut du plan (PLANS).
+ Source des valeurs par défaut : la gamme officielle de ``models/subscription.py``.
+ """
+ models: List[str] = Field(default_factory=list)
+ default_model: str = ""
+
+
+class AiTiersSettings(BaseModel):
+ essential: AiTierSettings = Field(
+ default_factory=lambda: AiTierSettings(models=list(DEFAULT_AI_MODELS_ESSENTIAL))
+ )
+ premium: AiTierSettings = Field(
+ default_factory=lambda: AiTierSettings(models=list(DEFAULT_AI_MODELS_PREMIUM))
+ )
+
+
+def _default_ai_tiers() -> AiTiersSettings:
+ return AiTiersSettings(
+ essential=AiTierSettings(models=list(DEFAULT_AI_MODELS_ESSENTIAL)),
+ premium=AiTierSettings(models=list(DEFAULT_AI_MODELS_PREMIUM)),
+ )
+
+
class SettingsConfig(BaseModel):
google: ProviderSettings = ProviderSettings(enabled=True)
google_cloud: ProviderSettings = ProviderSettings() # Cloud Translation API v2 (clé API)
@@ -923,11 +1080,47 @@ class SettingsConfig(BaseModel):
zai: ProviderSettings = ProviderSettings()
mistral: ProviderSettings = ProviderSettings() # OCR Mistral (PDF scannés)
smtp: SmtpSettings = SmtpSettings()
+ ai_tiers: AiTiersSettings = Field(default_factory=_default_ai_tiers)
fallback_chain: str = "google,google_cloud,openrouter,openrouter_premium,openai,deepseek,zai"
fallback_chain_classic: str = "google,google_cloud"
fallback_chain_llm: str = "openrouter,openrouter_premium,openai,deepseek,zai"
+def normalize_ai_tiers(ai_tiers: AiTiersSettings) -> AiTiersSettings:
+ """Déduplique, nettoie et complète un bloc ai_tiers avec la gamme officielle.
+
+ Lève ValueError si le défaut d'un palier appartient à la gamme de l'autre
+ palier (un défaut Essentielle doit rester un modèle Essentielle, et
+ inversement) : un utilisateur Pro ne doit jamais déclencher un modèle
+ Premium, et l'inverse serait une erreur de configuration.
+ """
+ essential_official = list(DEFAULT_AI_MODELS_ESSENTIAL)
+ premium_official = list(DEFAULT_AI_MODELS_PREMIUM)
+
+ def _fill(tier: AiTierSettings, official: List[str], other_official: List[str],
+ tier_name: str, other_name: str) -> AiTierSettings:
+ models = [m.strip() for m in (tier.models or []) if m and m.strip()]
+ models = list(dict.fromkeys(models)) # dédoublonnage, ordre conservé
+ if not models:
+ models = list(official)
+ default = (tier.default_model or "").strip()
+ if default and default not in models:
+ models.insert(0, default)
+ if default in other_official:
+ raise ValueError(
+ f"ai_tiers.{tier_name}.default_model « {default} » appartient à la "
+ f"gamme {other_name} : choisissez un modèle de la gamme {tier_name}."
+ )
+ return AiTierSettings(models=models, default_model=default)
+
+ return AiTiersSettings(
+ essential=_fill(ai_tiers.essential, essential_official, premium_official,
+ "essential", "Premium"),
+ premium=_fill(ai_tiers.premium, premium_official, essential_official,
+ "premium", "Essentielle"),
+ )
+
+
def load_settings() -> SettingsConfig:
try:
import json
@@ -983,10 +1176,10 @@ async def get_settings(admin_id: str = Depends(require_admin)):
return d
payload = settings.model_dump()
- # Essentielle : Gemini 3.5 Flash / DeepSeek Chat — meilleur rapport qualité/prix (juin 2026)
- payload["openrouter"] = _merge_env(settings.openrouter, key_env="OPENROUTER_API_KEY", model_env="OPENROUTER_MODEL", default_model="google/gemini-3.5-flash")
- # Premium : Claude Sonnet 4.6 — précision maximale sur documents complexes
- payload["openrouter_premium"] = _merge_env(settings.openrouter_premium, key_env="OPENROUTER_API_KEY", model_env="OPENROUTER_PREMIUM_MODEL", default_model="anthropic/claude-sonnet-4.6")
+ # Essentielle : DeepSeek V4 Flash / GLM-5.3 Flash / MiniMax M3 — meilleur rapport qualité/prix (sept. 2026)
+ payload["openrouter"] = _merge_env(settings.openrouter, key_env="OPENROUTER_API_KEY", model_env="OPENROUTER_MODEL", default_model="deepseek/deepseek-v4-flash")
+ # Premium : Claude 5 (Sonnet) — précision maximale sur documents complexes
+ payload["openrouter_premium"] = _merge_env(settings.openrouter_premium, key_env="OPENROUTER_API_KEY", model_env="OPENROUTER_PREMIUM_MODEL", default_model="anthropic/claude-sonnet-5")
payload["openai"] = _merge_env(settings.openai, key_env="OPENAI_API_KEY", model_env="OPENAI_MODEL", default_model="gpt-4o-mini")
payload["deepseek"] = _merge_env(settings.deepseek, key_env="DEEPSEEK_API_KEY", model_env="DEEPSEEK_MODEL", default_model="deepseek-chat")
payload["minimax"] = _merge_env(settings.minimax, key_env="MINIMAX_API_KEY", model_env="MINIMAX_MODEL", default_model="abab6.5s-chat")
@@ -1036,6 +1229,20 @@ async def get_settings(admin_id: str = Depends(require_admin)):
)
+def _ai_tiers_is_pristine(ai_tiers: AiTiersSettings) -> bool:
+ """True si le bloc ai_tiers reçu est le défaut vierge (client qui ne gère
+ pas les paliers : liste vide/par défaut et aucun modèle imposé)."""
+ def _is_default(tier: AiTierSettings, official: List[str]) -> bool:
+ models = [m.strip() for m in (tier.models or []) if m and m.strip()]
+ return (not models or models == list(official)) and not (
+ tier.default_model or ""
+ ).strip()
+
+ return _is_default(ai_tiers.essential, list(DEFAULT_AI_MODELS_ESSENTIAL)) and _is_default(
+ ai_tiers.premium, list(DEFAULT_AI_MODELS_PREMIUM)
+ )
+
+
@router.put("/settings")
async def update_settings(
settings: SettingsConfig, admin_id: str = Depends(require_admin)
@@ -1048,6 +1255,23 @@ async def update_settings(
if settings.smtp.password is not None:
settings.smtp.password = settings.smtp.password.strip() or None
+ # Paliers IA : un client qui renvoie le bloc par défaut vierge (liste vide ou
+ # gamme officielle, aucun défaut) n'écrase pas les paliers personnalisés
+ # existants — sinon chaque sauvegarde d'une autre page réinitialiserait la
+ # matrice « Modèles & abonnements ».
+ if _ai_tiers_is_pristine(settings.ai_tiers) and existing.ai_tiers != _default_ai_tiers():
+ settings.ai_tiers = existing.ai_tiers
+
+ # Paliers IA : dédoublonne et complète avec la gamme officielle (jamais vide).
+ # ValueError → 400 si un défaut appartient à la gamme du palier opposé.
+ try:
+ settings.ai_tiers = normalize_ai_tiers(settings.ai_tiers)
+ except ValueError as e:
+ return JSONResponse(
+ status_code=400,
+ content={"error": "INVALID_AI_TIER", "message": str(e)},
+ )
+
save_settings(settings)
logger.info(f"admin_settings_updated by {admin_id}")
return JSONResponse(
@@ -1971,3 +2195,579 @@ async def setup_stripe_webhook(request: Request, admin_id: str = Depends(require
"error": "STRIPE_API_ERROR",
"message": str(e),
})
+
+
+# ============================================================
+# MARKETING (relances par email — admin + désabonnement public)
+# Audiences : liste d'attente (data/waitlist.json), inactifs 30 j,
+# par plan (plan:) et tous les comptes. Historique et
+# désabonnements dans des fichiers JSON (même motif que waitlist).
+# ============================================================
+
+MARKETING_HISTORY_FILE = config.BASE_DIR / "data" / "marketing_emails.json"
+UNSUBSCRIBES_FILE = config.BASE_DIR / "data" / "unsubscribes.json"
+_marketing_lock = threading.Lock()
+# Empreintes des campagnes réelles en cours d'envoi (anti-simultanéité).
+_active_campaign_hashes: set = set()
+
+# Audiences officielles exposées à l'admin (« par plan » couvre tous les plans).
+MARKETING_BASE_AUDIENCES = ["waitlist", "inactive_30", "plan:pro", "all_users"]
+
+
+def _load_unsubscribes() -> List[dict]:
+ if not UNSUBSCRIBES_FILE.exists():
+ return []
+ try:
+ with open(UNSUBSCRIBES_FILE, encoding="utf-8") as f:
+ data = json.load(f)
+ return data if isinstance(data, list) else []
+ except Exception as e:
+ logger.warning(f"Failed to load unsubscribes: {e}")
+ return []
+
+
+def _save_unsubscribes(entries: List[dict]) -> None:
+ UNSUBSCRIBES_FILE.parent.mkdir(parents=True, exist_ok=True)
+ with open(UNSUBSCRIBES_FILE, "w", encoding="utf-8") as f:
+ json.dump(entries, f, indent=2, ensure_ascii=False)
+
+
+def _unsubscribed_emails() -> set:
+ return {
+ (e.get("email") or "").strip().lower()
+ for e in _load_unsubscribes()
+ if e.get("email")
+ }
+
+
+def _load_marketing_history() -> List[dict]:
+ if not MARKETING_HISTORY_FILE.exists():
+ return []
+ try:
+ with open(MARKETING_HISTORY_FILE, encoding="utf-8") as f:
+ data = json.load(f)
+ return data if isinstance(data, list) else []
+ except Exception as e:
+ logger.warning(f"Failed to load marketing history: {e}")
+ return []
+
+
+def _save_marketing_history(entries: List[dict]) -> None:
+ MARKETING_HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
+ with open(MARKETING_HISTORY_FILE, "w", encoding="utf-8") as f:
+ json.dump(entries, f, indent=2, ensure_ascii=False)
+
+
+def _all_accounts() -> List[dict]:
+ """Comptes utilisateurs (email, plan, activité, création) — base de données ou users.json."""
+ from services.auth_service import USE_DATABASE, DATABASE_AVAILABLE, load_users
+
+ accounts: List[dict] = []
+ seen: set = set()
+
+ def _add(email: Optional[str], plan: str = "free", docs: int = 0, created_at=None):
+ em = (email or "").strip().lower()
+ if not em or em in seen:
+ return
+ seen.add(em)
+ accounts.append(
+ {
+ "email": em,
+ "plan": plan,
+ "docs_this_month": docs,
+ "created_at": created_at,
+ }
+ )
+
+ if USE_DATABASE and DATABASE_AVAILABLE:
+ try:
+ from database.connection import get_sync_session
+ from database.models import User as DBUser
+
+ with get_sync_session() as session:
+ for u in session.query(DBUser).all():
+ pv = u.plan.value if hasattr(u.plan, "value") else str(u.plan)
+ _add(
+ u.email,
+ pv,
+ int(u.docs_translated_this_month or 0),
+ u.created_at,
+ )
+ except Exception as e:
+ logger.warning("Marketing audiences: DB users unavailable: %s", e)
+
+ for ud in load_users().values():
+ pr = ud.get("plan", "free")
+ pv = pr.value if hasattr(pr, "value") else str(pr)
+ try:
+ docs = int(ud.get("docs_translated_this_month", 0) or 0)
+ except (TypeError, ValueError):
+ docs = 0
+ _add(ud.get("email"), pv, docs, ud.get("created_at"))
+
+ return accounts
+
+
+def _parse_account_created_at(value) -> Optional[datetime]:
+ """created_at d'un compte (ISO string ou datetime) en datetime UTC."""
+ if value is None:
+ return None
+ if isinstance(value, datetime):
+ return value.replace(tzinfo=timezone.utc) if value.tzinfo is None else value
+ try:
+ parsed = datetime.fromisoformat(str(value))
+ return parsed.replace(tzinfo=timezone.utc) if parsed.tzinfo is None else parsed
+ except (TypeError, ValueError):
+ return None
+
+
+def _is_inactive_30(account: dict, cutoff_30d: datetime) -> bool:
+ """Inactif 30 j : aucune traduction ce mois ET compte créé il y a ≥ 30 jours
+ (un compte créé aujourd'hui n'est pas « inactif depuis 30 jours »).
+ Un compte sans date de création connue reste éligible (comportement prudent)."""
+ if account["docs_this_month"] != 0:
+ return False
+ created = _parse_account_created_at(account.get("created_at"))
+ return created is None or created <= cutoff_30d
+
+
+def _resolve_audience(audience: str) -> List[dict]:
+ """Destinataires d'une audience, désabonnés exclus et dédoublonnés."""
+ a = (audience or "").strip().lower()
+ unsub = _unsubscribed_emails()
+ recipients: List[dict] = []
+
+ if a == "waitlist":
+ from routes.waitlist_routes import _load_waitlist
+
+ for entry in _load_waitlist():
+ recipients.append({"email": (entry.get("email") or "").strip().lower()})
+ else:
+ accounts = _all_accounts()
+ if a == "all_users":
+ recipients = accounts
+ elif a == "inactive_30":
+ # Approximation « inactif » : aucune traduction ce mois-ci ET compte
+ # créé il y a au moins 30 jours (un nouveau compte n'est pas inactif).
+ cutoff = datetime.now(timezone.utc) - timedelta(days=30)
+ recipients = [acc for acc in accounts if _is_inactive_30(acc, cutoff)]
+ elif a.startswith("plan:"):
+ plan_id = a.split(":", 1)[1].strip()
+ recipients = [acc for acc in accounts if acc["plan"] == plan_id]
+ else:
+ return []
+
+ # Dédoublonnage + exclusion des désabonnés (jamais d'envoi à un désabonné).
+ final: List[dict] = []
+ seen: set = set()
+ for r in recipients:
+ em = (r.get("email") or "").strip().lower()
+ if not em or em in seen or em in unsub:
+ continue
+ seen.add(em)
+ final.append(r)
+ return final
+
+
+def _marketing_content_hash(subject: str, html: str) -> str:
+ return hashlib.sha256(f"{subject}\n{html}".encode("utf-8")).hexdigest()
+
+
+def _unsubscribe_link(email: str) -> str:
+ base = (
+ os.getenv("FRONTEND_URL", "http://localhost:3000").rstrip("/")
+ )
+ from urllib.parse import quote
+
+ return f"{base}/api/v1/marketing/unsubscribe?email={quote(email)}"
+
+
+def _marketing_extra_headers(email: str) -> dict:
+ """En-têtes de délivrabilité (RFC 2369 / RFC 8058) : désabonnement en un clic."""
+ return {
+ "List-Unsubscribe": f"<{_unsubscribe_link(email)}>",
+ "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
+ }
+
+
+def _append_unsubscribe_footer(html: str, email: str) -> str:
+ link = _unsubscribe_link(email)
+ footer = (
+ ''
+ "Vous recevez cet email car vous avez un compte Wordly.art. "
+ f'
Me désabonner '
+ )
+ match = re.search(r"