perf+security: fix build, secure downloads, dedupe translations, refactor i18n
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m49s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m49s
Frontend:
- Fix Framer Motion / motion-dom build error by pinning framer-motion to
11.18.2 (compatible with React 19 and Next.js 16).
- Add cross-env and build:local script to bypass standalone symlink errors
on Windows without Developer Mode.
- Allow NEXT_OUTPUT=default to disable standalone output for local builds.
- Refactor i18n: split 14,177-line src/lib/i18n.tsx into per-locale,
per-namespace JSON files under src/lib/i18n/messages/.
- Load English synchronously; other locales loaded on demand via dynamic
imports (reduces initial bundle, improves maintainability).
- Remove unused next-intl message files src/messages/en.json and fr.json.
Backend:
- Remove insecure legacy /api/v1/download/{filename} and /api/v1/cleanup/{filename}
endpoints. The job-based /api/v1/download/{job_id} already enforces ownership.
- Deduplicate texts in TranslationService.translate_batch before sending them
to the provider, reducing API calls for repeated strings.
- Pin httpx to <0.28 to fix TestClient incompatibility with starlette 0.35.1.
- Add pytest-cov and ruff dev dependencies/config.
DevOps:
- Remove hardcoded Grafana password from docker-compose.yml and
docker-compose.monitoring.yml; use GRAFANA_PASSWORD env var.
- Change default TRANSLATION_SERVICE from ollama to google in
docker-compose.yml (Ollama is an optional profile).
- Add GRAFANA_PASSWORD to .env.example.
- Add .coverage and frontend/pnpm-workspace.yaml to .gitignore.
Tests:
- Update API versioning tests for removed legacy endpoints.
- Add tests/test_translation_service.py for deduplication behavior.
Verified:
- pnpm run build:local passes.
- uv run pytest tests/test_providers/* tests/test_translation_service.py
tests/test_story_3_5_api_versioning.py tests/test_download_endpoint.py
tests/test_translators/test_excel_translator.py: provider/translator tests
pass; one pre-existing French error-message test still fails (message is
returned in English, unrelated to this change).
This commit is contained in:
111
frontend/src/lib/i18n/messages/fa/dashboard.json
Normal file
111
frontend/src/lib/i18n/messages/fa/dashboard.json
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"dashboard.nav.translate": "ترجمه",
|
||||
"dashboard.nav.profile": "پروفایل من",
|
||||
"dashboard.nav.settings": "تنظیمات",
|
||||
"dashboard.nav.context": "زمینه",
|
||||
"dashboard.nav.services": "خدمات",
|
||||
"dashboard.nav.apiKeys": "کلیدهای API",
|
||||
"dashboard.nav.glossaries": "واژهنامهها",
|
||||
"dashboard.header.title": "داشبورد",
|
||||
"dashboard.header.subtitle": "مدیریت ترجمههای شما",
|
||||
"dashboard.header.toggleMenu": "منو",
|
||||
"dashboard.header.profileTitle": "پروفایل من",
|
||||
"dashboard.sidebar.theme": "پوسته",
|
||||
"dashboard.sidebar.signOut": "خروج",
|
||||
"dashboard.sidebar.backHome": "بازگشت به صفحه اصلی",
|
||||
"dashboard.sidebar.upgradeToPro": "ارتقا به Pro ←",
|
||||
"dashboard.translate.pageTitle": "ترجمه یک سند",
|
||||
"dashboard.translate.pageSubtitle": "یک فایل وارد کنید و زبان هدف را انتخاب کنید",
|
||||
"dashboard.translate.errorNotificationTitle": "خطا",
|
||||
"dashboard.translate.dropzone.uploadAria": "منطقه رها کردن فایل",
|
||||
"dashboard.translate.dropzone.title": "فایل خود را اینجا بکشید و رها کنید",
|
||||
"dashboard.translate.dropzone.subtitle": "یا کلیک کنید برای انتخاب (DOCX, XLSX, PPTX, PDF)",
|
||||
"dashboard.translate.dropzone.replaceFile": "جایگزینی فایل",
|
||||
"dashboard.translate.language.source": "زبان مبدأ",
|
||||
"dashboard.translate.language.target": "زبان مقصد",
|
||||
"dashboard.translate.language.loading": "در حال بارگذاری زبانها…",
|
||||
"dashboard.translate.language.autoDetect": "تشخیص خودکار",
|
||||
"dashboard.translate.language.selectPlaceholder": "انتخاب…",
|
||||
"dashboard.translate.language.loadErrorPrefix": "بارگذاری زبانها ناموفق بود",
|
||||
"dashboard.translate.provider.loading": "در حال بارگذاری ارائهدهندهها…",
|
||||
"dashboard.translate.provider.noneConfigured": "هیچ ارائهدهندهای پیکربندی نشده",
|
||||
"dashboard.translate.provider.modelTitle": "مدل",
|
||||
"dashboard.translate.provider.sectionTitle": "ارائهدهنده",
|
||||
"dashboard.translate.provider.llmDivider": "هوش مصنوعی · آگاه به زمینه",
|
||||
"dashboard.translate.provider.llmDividerPro": "هوش مصنوعی · آگاه به زمینه (حرفهای)",
|
||||
"dashboard.translate.provider.upgrade": "ارتقا به حرفهای",
|
||||
"dashboard.translate.provider.upgradeSuffix": "برای دسترسی به ترجمه هوش مصنوعی",
|
||||
"dashboard.translate.trust.zeroRetention": "بدون نگهداری",
|
||||
"dashboard.translate.trust.deletedAfter": "فایلها پس از پردازش حذف میشوند",
|
||||
"dashboard.translate.actions.uploading": "در حال بارگذاری…",
|
||||
"dashboard.translate.actions.translate": "ترجمه",
|
||||
"dashboard.translate.actions.filePrefix": "فایل: ",
|
||||
"dashboard.translate.actions.cancel": "انصراف",
|
||||
"dashboard.translate.actions.tryAgain": "تلاش دوباره",
|
||||
"dashboard.translate.steps.uploading": "در حال بارگذاری فایل…",
|
||||
"dashboard.translate.steps.starting": "در حال شروع ترجمه…",
|
||||
"dashboard.translate.complete.title": "ترجمه کامل شد!",
|
||||
"dashboard.translate.complete.descNamed": "فایل {name} شما با موفقیت ترجمه شد.",
|
||||
"dashboard.translate.complete.descGeneric": "فایل شما با موفقیت ترجمه شد.",
|
||||
"dashboard.translate.complete.downloading": "در حال دانلود…",
|
||||
"dashboard.translate.complete.download": "دانلود",
|
||||
"dashboard.translate.complete.newTranslation": "ترجمه جدید",
|
||||
"dashboard.translate.complete.toastOkTitle": "موفقیت",
|
||||
"dashboard.translate.complete.toastOkDesc": "{name} با موفقیت بارگیری شد.",
|
||||
"dashboard.translate.complete.toastFailTitle": "ناموفق",
|
||||
"dashboard.translate.complete.toastFailDesc": "ترجمه ناموفق بود. لطفاً دوباره تلاش کنید.",
|
||||
"dashboard.translate.sourceDocument": "سند مبدأ",
|
||||
"dashboard.translate.configuration": "پیکربندی",
|
||||
"dashboard.translate.translating": "در حال ترجمه",
|
||||
"dashboard.translate.liveMonitor": "مانیتور زنده",
|
||||
"dashboard.translate.summary": "خلاصه",
|
||||
"dashboard.translate.engine": "موتور",
|
||||
"dashboard.translate.confidence": "اطمینان",
|
||||
"dashboard.translate.cancel": "لغو",
|
||||
"dashboard.translate.segments": "بخشها",
|
||||
"dashboard.translate.characters": "نویسهها",
|
||||
"dashboard.translate.elapsed": "گذشته",
|
||||
"dashboard.translate.segPerMin": "بخش/دقیقه",
|
||||
"dashboard.translate.highQuality": "کیفیت بالا",
|
||||
"dashboard.translate.quality": "کیفیت",
|
||||
"dashboard.translate.completed": "ترجمه انجام شد",
|
||||
"dashboard.translate.replace": "جایگزینی",
|
||||
"dashboard.translate.pdfMode.title": "حالت ترجمه PDF",
|
||||
"dashboard.translate.pdfMode.preserveLayout": "حفظ چیدمان",
|
||||
"dashboard.translate.pdfMode.textOnly": "فقط متن",
|
||||
"dashboard.translate.pdfMode.preserveLayoutDesc": "تصاویر، جداول و قالببندی را حفظ میکند. مناسب PDFهای ساده.",
|
||||
"dashboard.translate.pdfMode.textOnlyDesc": "تمام متن را کاملاً ترجمه میکند. خروجی تمیز، بدون مشکل چیدمان.",
|
||||
"dashboard.translate.pipeline.upload": "بارگذاری",
|
||||
"dashboard.translate.pipeline.analyze": "تحلیل",
|
||||
"dashboard.translate.pipeline.translate": "ترجمه",
|
||||
"dashboard.translate.pipeline.rebuild": "بازسازی",
|
||||
"dashboard.translate.pipeline.finalize": "نهاییسازی",
|
||||
"dashboard.translate.progress.processingFallback": "در حال پردازش…",
|
||||
"dashboard.translate.progress.connectionLost": "اتصال قطع شد. در حال تلاش مجدد…",
|
||||
"dashboard.translate.progress.failedTitle": "ترجمه ناموفق",
|
||||
"dashboard.translate.error.unexpected": "خطای غیرمنتظرهای رخ داد. لطفاً دوباره تلاش کنید.",
|
||||
"dashboard.translate.error.noResult": "ترجمه نتیجهای نداشت. تأیید کنید که سند حاوی متن است، سپس دوباره تلاش کنید یا موتور دیگری انتخاب کنید.",
|
||||
"dashboard.translate.error.apiKey": "کلید API نامعتبر یا ناموجود است. با مدیر تماس بگیرید تا کلیدها را پیکربندی کند.",
|
||||
"dashboard.translate.error.quota": "محدودیت استفاده به پایان رسیده. چند دقیقه دیگر دوباره تلاش کنید یا موتور دیگری انتخاب کنید.",
|
||||
"dashboard.translate.error.timeout": "اتصال به سرویس ترجمه منقضی شد. شبکه خود را بررسی کنید و دوباره تلاش کنید.",
|
||||
"dashboard.translate.error.sessionExpired": "نشست منقضی شده. برای شروع مجدد ترجمه روی تلاش مجدد کلیک کنید.",
|
||||
"dashboard.translate.error.empty": "سند خالی به نظر میرسد یا متن قابل ترجمهای ندارد (تصویر PDF اسکن شده؟).",
|
||||
"dashboard.translate.error.unsupported": "فرمت فایل پشتیبانی نمیشود یا فایل خراب است.",
|
||||
"dashboard.translate.error.connection": "اتصال قطع شد. شبکه خود را بررسی کنید و دوباره تلاش کنید.",
|
||||
"dashboard.translate.error.generic": "ترجمه ناموفق: {detail}",
|
||||
"dashboard.translate.error.title": "ترجمه ناموفق",
|
||||
"dashboard.translate.retry": "تلاش مجدد ترجمه",
|
||||
"dashboard.translate.newFile": "فایل جدید",
|
||||
"dashboard.translate.modeAI": "حالت هوش مصنوعی",
|
||||
"dashboard.translate.modeClassic": "حالت کلاسیک",
|
||||
"dashboard.translate.glossaryLLMHint": "واژهنامهها در حالت هوش مصنوعی موجودند",
|
||||
"dashboard.translate.submitting": "در حال ارسال...",
|
||||
"dashboard.translate.submit": "شروع ترجمه",
|
||||
"dashboard.translate.noFile": "ابتدا یک فایل آپلود کنید",
|
||||
"dashboard.translate.noTargetLang": "زبان مقصد را انتخاب کنید",
|
||||
"dashboard.topbar.interfaceLabel": "رابط ترجمه",
|
||||
"dashboard.topbar.premiumAccess": "دسترسی ویژه",
|
||||
"dashboard.checkoutSyncError": "خطا در همگامسازی پرداخت.",
|
||||
"dashboard.networkRefresh": "خطای شبکه. لطفاً صفحه را تازهسازی کنید.",
|
||||
"dashboard.continueToTranslate": "ادامه به ترجمه"
|
||||
}
|
||||
Reference in New Issue
Block a user