feat: production deployment - full update with providers, admin, glossaries, pricing, tests

Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Sepehr Ramezani
2026-04-25 15:01:47 +02:00
parent 2ba4fedfc8
commit 26bd096a06
1178 changed files with 136435 additions and 3047 deletions

View File

@@ -12,7 +12,10 @@
"free": "Free",
"popular": "Popular",
"month": "month",
"onRequest": "On Request"
"onRequest": "On Request",
"monthly": "Monthly",
"yearly": "Yearly",
"save20": "Save 20%"
},
"nav": {
"features": "Features",
@@ -62,35 +65,43 @@
"pricing": {
"title": "Simple and Transparent Pricing",
"subtitle": "Start for free, scale as you need.",
"free": {
"name": "Free",
"description": "For occasional needs.",
"price": "$0",
"starter": {
"name": "Starter",
"description": "For individuals and small projects.",
"priceMonthly": "$9",
"priceYearly": "$7.20",
"billedYearly": "Billed $86.40 / year",
"features": [
"5 documents / month",
"Word & Excel only",
"Community support"
"50 documents / month",
"Standard translation (Google/DeepL)",
"Files up to 10 MB",
"Email support"
]
},
"pro": {
"name": "Pro",
"description": "For demanding professionals.",
"price": "$29",
"description": "For growing professionals.",
"priceMonthly": "$19",
"priceYearly": "$15.20",
"billedYearly": "Billed $182.40 / year",
"features": [
"50 documents / month",
"All formats (PPTX included)",
"Advanced AI models (GPT-4)",
"Priority support"
"200 documents / month",
"Essential AI (DeepSeek V3)",
"Files up to 25 MB",
"Custom glossaries"
]
},
"enterprise": {
"name": "Enterprise",
"business": {
"name": "Business",
"description": "For large teams.",
"priceMonthly": "$49",
"priceYearly": "$39.20",
"billedYearly": "Billed $470.40 / year",
"features": [
"Unlimited documents",
"API Access",
"SSO & Advanced Security",
"Dedicated Account Manager"
"1,000 documents / month",
"Premium AI (Claude Haiku)",
"API Access (10K calls)",
"Dedicated support"
]
}
},
@@ -103,10 +114,277 @@
"privacy": "Privacy",
"terms": "Terms",
"contact": "Contact",
"copyright": "© 2024 Office Translator. All rights reserved."
"copyright": "© 2026 Office Translator. All rights reserved."
},
"cookieConsent": {
"title": "Cookies on Office Translator",
"description": "We use essential cookies so the app works (session, security, language). With your permission, we also use optional cookies to measure traffic and improve the product. Choose “Accept all” to allow optional cookies, or “Essential only” to refuse them.",
"acceptAll": "Accept all",
"essentialOnly": "Essential only",
"learnMore": "Learn more on our site",
"footerHint": "You can clear site data in your browser at any time to see this message again."
},
"glossaries": {
"title": "Glossaries",
"description": "Manage custom terminology for your LLM translations.",
"createNew": "Create New Glossary",
"yourGlossaries": "Your Glossaries",
"yourGlossariesDesc": "Create and manage glossaries for consistent translations",
"count_one": "{count} glossary",
"count_other": "{count} glossaries",
"defineTerms": "Define term pairs to customize your LLM translations",
"empty": "No glossaries yet",
"emptyDesc": "Create your first glossary to customize translations",
"aboutTitle": "About Glossaries",
"aboutDesc": "Glossaries let you define custom terminology for your translations. When using LLM translation modes, your terms will be applied to ensure consistent translations.",
"aboutFormat": "Format: Each term has a source (original) and target (translation) pair.",
"dialog": {
"title": "New Glossary",
"description": "Import a predefined template, upload a file with your industry jargon, or enter terms manually.",
"nameLabel": "Glossary name",
"namePlaceholder": "e.g. Legal Glossary FR-EN",
"tabTemplates": "Predefined templates",
"tabFile": "Import file",
"tabManual": "Manual entry",
"templatesDesc": "Choose a predefined glossary covering your industry's terminology.",
"templatesEmpty": "No templates available.",
"terms": "terms",
"cancel": "Cancel",
"importing": "Importing…",
"creating": "Creating…",
"importBtn": "Import ({count} terms)",
"createBtn": "Create ({count} terms)",
"createEmpty": "Create glossary",
"selectPrompt": "Select a template",
"dropTitle": "Drop your file here",
"dropOr": "or click to browse",
"dropFormats": "CSV, XLSX, XLS, ODS • max 500 terms",
"parsing": "Parsing file…",
"termsImported": "{count} terms imported",
"changeFile": "Change file",
"retry": "Retry",
"formatTitle": "Expected format:",
"formatDesc": "Two columns with headers:",
"formatNote": "The first row can be a header (source/target) or raw data.",
"errorFormat": "Unsupported format. Use CSV, XLSX, XLS or ODS.",
"errorSize": "File too large (max {max} MB). Split into smaller files.",
"errorEmpty": "No valid terms found. Check that your file has two columns (source, target).",
"errorRead": "Unable to read the file. Check the format."
},
"toast": {
"created": "Glossary created",
"createdDesc": "\"{name}\" has been created successfully.",
"imported": "Template imported",
"importedDesc": "\"{name}\" has been imported successfully.",
"updated": "Glossary updated",
"updatedDesc": "\"{name}\" has been updated successfully.",
"deleted": "Glossary deleted",
"deletedDesc": "The glossary has been deleted successfully.",
"error": "Error",
"errorCreate": "Failed to create glossary. Please try again.",
"errorImport": "Failed to import template. Please try again.",
"errorUpdate": "Failed to update glossary. Please try again.",
"errorDelete": "Failed to delete glossary. Please try again."
},
"card": {
"term": "term",
"terms": "terms",
"created": "Created"
},
"termEditor": {
"sourceTerm": "Source Term",
"targetTranslation": "Target Translation",
"addTerm": "Add Term",
"maxReached": "Maximum {max} terms per glossary reached."
}
},
"auth": {
"loading": "Loading…",
"or": "or",
"brandName": "Office Translator",
"google": {
"continue": "Continue with Google",
"signUp": "Sign up with Google",
"connecting": "Connecting…",
"errorGeneric": "Something went wrong with Google sign-in.",
"errorFailed": "Google sign-in failed. Please try again.",
"devHint": "To enable Google, add {envKey} in {envFile} (see {exampleFile})."
},
"login": {
"title": "Welcome back",
"subtitle": "Sign in to keep translating",
"email": "Email",
"password": "Password",
"emailPlaceholder": "you@example.com",
"forgotPassword": "Forgot password?",
"signingIn": "Signing in…",
"signIn": "Sign in",
"errorTitle": "Sign-in error",
"noAccount": "Don't have an account?",
"signUpFree": "Sign up for free"
},
"register": {
"title": "Create an account",
"subtitle": "Start translating for free",
"name": "Name",
"namePlaceholder": "Your name",
"email": "Email address",
"emailPlaceholder": "you@example.com",
"password": "Password",
"confirmPassword": "Confirm password",
"createAccount": "Create my account",
"creating": "Creating account…",
"hasAccount": "Already have an account?",
"signIn": "Sign in",
"termsPrefix": "By creating an account, you agree to our",
"termsService": "terms of service",
"registerFailed": "Registration failed",
"errors": {
"nameMin": "Name must be at least 2 characters",
"emailInvalid": "Invalid email address",
"passwordShort": "Password too short (minimum 8 characters)",
"passwordMismatch": "Passwords do not match"
},
"passwordStrength": {
"label": "Strength",
"weak": "Weak",
"medium": "Medium",
"strong": "Strong"
},
"toggle": {
"hide": "Hide",
"show": "Show",
"hidePassword": "Hide password",
"showPassword": "Show password"
}
}
},
"dashboard": {
"nav": {
"overview": "Overview",
"profile": "My profile",
"translate": "Translate",
"apiKeys": "API keys",
"glossaries": "Glossaries"
},
"header": {
"title": "Dashboard",
"subtitle": "Manage your API and translation settings",
"toggleMenu": "Toggle menu",
"profileTitle": "My profile"
},
"sidebar": {
"theme": "Theme",
"signOut": "Sign out",
"backHome": "Back to home"
},
"tier": {
"free": "Free",
"starter": "Starter",
"pro": "Pro",
"business": "Business",
"enterprise": "Enterprise"
},
"translate": {
"errorNotificationTitle": "Translation error",
"pageTitle": "Translate document",
"pageSubtitle": "Upload an Excel, Word, or PowerPoint file, choose languages and provider, then download your translated file — layout preserved.",
"document": {
"title": "Document",
"descReady": "File ready — you can replace it or continue below.",
"descEmpty": "Drag and drop or browse. We support Office Open XML formats.",
"supportedFormats": "Supported formats: Excel (.xlsx), Word (.docx), PowerPoint (.pptx)"
},
"settings": {
"title": "Translation settings",
"description": "Languages and engine. LLM options require Pro when applicable.",
"howItWorks": "How it works",
"step1": "Add your file in the panel on the left (or above on small screens).",
"step2": "Pick source and target languages.",
"step3": "Choose a translation provider, then run the job.",
"runHint": "When you are ready, we upload the file and start translation. You can cancel while it runs."
},
"actions": {
"translate": "Translate document",
"uploading": "Uploading…",
"cancel": "Cancel",
"tryAgain": "Try again",
"statusComplete": "Translation complete",
"statusIssue": "Translation issue",
"statusProgress": "In progress",
"filePrefix": "File:"
},
"trust": {
"zeroRetention": "Zero data retention",
"deletedAfter": "Files deleted after 60 minutes"
},
"dropzone": {
"title": "Drag & drop your .xlsx, .docx, or .pptx file here",
"subtitle": "or click to browse",
"uploadAria": "Upload file",
"replaceFile": "Replace file"
},
"language": {
"loadErrorPrefix": "Failed to load languages:",
"source": "Source language",
"target": "Target language",
"loading": "Loading…",
"autoDetect": "Auto-detect",
"selectPlaceholder": "Select language"
},
"provider": {
"loading": "Loading providers…",
"noneConfigured": "No providers are configured. Ask your administrator to enable at least one in the admin settings.",
"sectionTitle": "Translation provider",
"llmDivider": "LLM · Context-aware",
"llmDividerPro": "Pro",
"upgrade": "Upgrade to Pro",
"upgradeSuffix": "to use LLM-powered translation.",
"modelTitle": "Model (set by admin):"
},
"progress": {
"failedTitle": "Translation failed",
"processingFallback": "Processing…",
"connectionLost": "Connection lost. Retrying…",
"timeSeconds": "{seconds}s remaining",
"timeMinutes": "{minutes} min remaining",
"timeMixed": "{minutes}m {seconds}s remaining",
"ariaProgress": "Translation progress"
},
"complete": {
"title": "Translation complete!",
"descNamed": "“{name}” has been translated successfully.",
"descGeneric": "Your document has been translated successfully.",
"downloading": "Downloading…",
"download": "Download translated file",
"newTranslation": "New translation",
"toastOkTitle": "Download complete",
"toastOkDesc": "{name} has been downloaded successfully.",
"toastFailTitle": "Download failed",
"toastFailDesc": "Could not download the translated file."
},
"steps": {
"uploading": "Uploading file…",
"starting": "Starting translation…",
"translating": "Translating…"
},
"errors": {
"jobNotFound": "Translation job not found",
"translationFailed": "Translation failed",
"connectionLost": "Lost connection to the translation service. Check your internet connection and try again."
}
}
},
"landing": {
"originalLabel": "Original",
"translatedLabel": "Translated"
"heroDoc": {
"fileName": "Technical_Brief_HVAC.docx",
"ribbonFile": "File",
"ribbonHome": "Home",
"ribbonInsert": "Insert",
"ribbonLayout": "Layout",
"badgeOriginal": "Original (EN)",
"badgeTranslated": "Translation (FR)",
"caption": "One Word document: English on the left, French on the right — same layout preserved."
}
}
}