fix(ui): design critique overhaul — a11y, honest metrics, i18n repair (critique 2026-08-30)
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m51s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m51s
P0 a11y: keyboard-accessible dropzone (role/tabIndex/Enter-Space), ARIA combobox + listbox pattern for language selector, role=switch on glossary toggle, role=status live region on notifications, aria-labels on password toggles, visible-on-focus close buttons, RTL logical positioning (start-*). Trust: third-party Memento promo removed from translate page, sidebar and all 13 locales; fabricated stats (99.9%, Turbo, computed layout-integrity bars) replaced with real measurements incl. API estimated remaining time; silent download failure now surfaces an error notification. Honesty: fake 100-byte file injections removed (format chips are now informational); cancel-that-doesn't renamed 'Back to start' with hint; Enterprise contact placeholder replaced with contact@wordly.art. i18n: t() no longer returns raw keys (empty string + defaultValue support, ~30 dead || fallbacks now work); ~170 new keys EN+FR across new reviews/ teams namespaces, glossaries context tab, translate monitor, settings, services, pricing, landing, fileUploader; split-key italic titles replace lastIndexOf() surgery (zh/ja-safe); key-audit script added 0 missing. Flow: active job persisted across refresh with polling resume (24h TTL); client-side recent-jobs history with review links; review page linked from complete state; settings/services added to dashboard nav; Business/ Enterprise regain glossary access (tier gate unified). Typeset (sober-tool direction): 7.5-9px labels raised to 10-12px, /30 opacity to /45-/55, uppercase tracking reduced, trust footer legible, country flags removed from language switcher, localized dates. Cleanup: 5 orphaned translate components, dead site header/footer, fossil tailwind.config.js, PipelineStepper, duplicate pill+H1 titles, two-step confirm for cache clear, dead landing footer links. Verified: next build exit 0, vitest 9/9, eslint 64 errors = HEAD (no regression, -3 warnings), detector 4 -> 3 findings.
This commit is contained in:
@@ -158,8 +158,17 @@ export function I18nProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const t = useCallback(
|
||||
(key: string, params?: TranslationParams): string => {
|
||||
const msg = messages[key] ?? enMessages[key] ?? key;
|
||||
return interpolate(msg, params);
|
||||
const msg = messages[key] ?? enMessages[key];
|
||||
if (msg === undefined) {
|
||||
// Missing everywhere: honour an explicit defaultValue, else return ""
|
||||
// so `t(key) || fallback` call sites work and raw keys never reach the UI.
|
||||
return params && "defaultValue" in params
|
||||
? String(params.defaultValue)
|
||||
: "";
|
||||
}
|
||||
const rest = { ...params };
|
||||
delete rest.defaultValue;
|
||||
return interpolate(msg, rest);
|
||||
},
|
||||
[messages],
|
||||
);
|
||||
|
||||
@@ -15,7 +15,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -41,7 +40,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "اكتشف Memento",
|
||||
"memento.slogan": "Memento ليس مجرد تطبيق ملاحظات. إنه نظام بيئي ذكي يربط ويحلل ويطور أفكارك في الوقت الفعلي باستخدام 6 وكلاء ذكاء اصطناعي وبحث دلالي متقدم.",
|
||||
"memento.ctaFree": "ابدأ مجاناً",
|
||||
"memento.ctaMore": "اعرف المزيد",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Entdecken Sie Memento",
|
||||
"memento.slogan": "Memento ist mehr als nur eine Notizen-App. Es ist ein intelligentes Ökosystem, das Ihre Ideen in Echtzeit verbindet, analysiert und weiterentwickelt – mit 6 KI-Agenten und semantischer Suche.",
|
||||
"memento.ctaFree": "Kostenlos starten",
|
||||
"memento.ctaMore": "Mehr erfahren",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -112,5 +112,6 @@
|
||||
"dashboard.checkoutSyncError": "Error syncing payment.",
|
||||
"dashboard.networkRefresh": "Network error. Please refresh the page.",
|
||||
"dashboard.continueToTranslate": "Continue to translation",
|
||||
"dashboard.nav.teams": "Team"
|
||||
"dashboard.nav.teams": "Team",
|
||||
"dashboard.loading": "Loading…"
|
||||
}
|
||||
|
||||
@@ -27,5 +27,7 @@
|
||||
"fileUploader.foundTexts": "Found {count} texts to translate",
|
||||
"fileUploader.translatingItem": "Translating {current}/{total}: \\\"{preview}\\\"",
|
||||
"fileUploader.reconstructing": "Reconstructing document...",
|
||||
"fileUploader.translatingLocally": "Translating locally with WebLLM..."
|
||||
"fileUploader.translatingLocally": "Translating locally with WebLLM...",
|
||||
"fileUploader.error.invalidFormat": "Unsupported format. Accepted: .xlsx, .docx, .pptx, .pdf",
|
||||
"fileUploader.error.tooLarge": "File too large (max 50 MB)"
|
||||
}
|
||||
|
||||
@@ -189,5 +189,36 @@
|
||||
"glossaries.detail.importedTitle": "Imported",
|
||||
"glossaries.detail.importedDesc": "{count} terms imported.",
|
||||
"glossaries.detail.importErrorTitle": "Read error",
|
||||
"glossaries.detail.importErrorDesc": "Unable to read the file."
|
||||
"glossaries.detail.importErrorDesc": "Unable to read the file.",
|
||||
"glossaries.titleBase": "Glossaries &",
|
||||
"glossaries.titleAccent": "Context",
|
||||
"glossaries.new": "New glossary",
|
||||
"glossaries.info.title": "To use a glossary in a translation:",
|
||||
"glossaries.info.body": "click “Use” on the desired card. You will be redirected to the Translate page with this glossary already selected.",
|
||||
"glossaries.info.cta": "Go translate",
|
||||
"glossaries.context.title": "Context guidelines",
|
||||
"glossaries.context.desc": "These instructions are automatically sent to the AI before each translation in Pro LLM mode.",
|
||||
"glossaries.context.unsaved": "Unsaved",
|
||||
"glossaries.context.active": "Active",
|
||||
"glossaries.context.inactive": "Inactive",
|
||||
"glossaries.context.suggestions": "Quick suggestions — click to add",
|
||||
"glossaries.context.placeholder": "e.g. You are translating technical HVAC documents. Use precise engineering terminology and a professional tone…",
|
||||
"glossaries.context.chars": "{count} characters",
|
||||
"glossaries.context.noPrompt": "No guideline saved",
|
||||
"glossaries.context.clear": "Clear all",
|
||||
"glossaries.context.saving": "Saving…",
|
||||
"glossaries.context.saved": "Saved",
|
||||
"glossaries.context.save": "Save",
|
||||
"glossaries.activeTarget": "Active translation:",
|
||||
"glossaries.searchPlaceholder": "Search a glossary…",
|
||||
"glossaries.countMany": "{count} glossaries — click a card to edit",
|
||||
"glossaries.countNone": "No glossary yet — create one to get started",
|
||||
"glossaries.termsOne": "1 term",
|
||||
"glossaries.termsMany": "{count} terms",
|
||||
"glossaries.badge.otherLang": "Other language",
|
||||
"glossaries.card.edit": "Edit",
|
||||
"glossaries.card.use": "Use",
|
||||
"glossaries.createFirst": "Create my first glossary",
|
||||
"glossaries.noResults": "No results for “{query}”",
|
||||
"glossaries.card.terms": "terms"
|
||||
}
|
||||
|
||||
@@ -16,15 +16,16 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
import providerTheme from "./providerTheme.json";
|
||||
import register from "./register.json";
|
||||
import resetPassword from "./resetPassword.json";
|
||||
import reviews from "./reviews.json";
|
||||
import services from "./services.json";
|
||||
import settings from "./settings.json";
|
||||
import teams from "./teams.json";
|
||||
import translate from "./translate.json";
|
||||
import translateComplete from "./translateComplete.json";
|
||||
|
||||
@@ -44,15 +45,16 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
...providerTheme,
|
||||
...register,
|
||||
...resetPassword,
|
||||
...reviews,
|
||||
...services,
|
||||
...settings,
|
||||
...teams,
|
||||
...translate,
|
||||
...translateComplete,
|
||||
};
|
||||
|
||||
@@ -148,5 +148,6 @@
|
||||
"landing.translate.supportedFormats": "DOCX, XLSX, PPTX or PDF files supported",
|
||||
"landing.translate.aiAnalysis": "Active AI Analysis",
|
||||
"landing.translate.processing": "Processing",
|
||||
"landing.translate.preservingLayout": "Your layout is being preserved"
|
||||
"landing.translate.preservingLayout": "Your layout is being preserved",
|
||||
"landing.formats.pill": "COMPATIBILITY"
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
"langSelector.noResults": "No results",
|
||||
"langSelector.source": "Source",
|
||||
"langSelector.target": "Target",
|
||||
"langSelector.swap": "Swap"
|
||||
"langSelector.swap": "Swap",
|
||||
"langSelector.changeLanguage": "Change language"
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Discover Memento",
|
||||
"memento.slogan": "Memento is more than just a notes application. It's an intelligent ecosystem that connects, analyzes, and develops your ideas in real-time using 6 AI agents and cutting-edge semantic search.",
|
||||
"memento.ctaFree": "Start for free",
|
||||
"memento.ctaMore": "Learn more",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -147,5 +147,10 @@
|
||||
"pricing.toast.paymentError": "Error creating payment.",
|
||||
"pricing.dashboard": "Dashboard",
|
||||
"pricing.okSymbol": "✓",
|
||||
"pricing.errSymbol": "✕"
|
||||
"pricing.errSymbol": "✕",
|
||||
"pricing.error.server": "Server error {status}",
|
||||
"pricing.enterprise.subject": "Enterprise plan enquiry",
|
||||
"pricing.toast.close": "Close",
|
||||
"pricing.header.titleBase": "A plan for",
|
||||
"pricing.header.titleAccent": "every need"
|
||||
}
|
||||
|
||||
32
frontend/src/lib/i18n/messages/en/reviews.json
Normal file
32
frontend/src/lib/i18n/messages/en/reviews.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"reviews.title": "Review",
|
||||
"reviews.subtitle": "{file} — {total} segments ({pending} to review, {approved} approved, {edited} edited)",
|
||||
"reviews.approveAll": "Approve all",
|
||||
"reviews.importXliff": "Import XLIFF",
|
||||
"reviews.rebuild": "Rebuild & download",
|
||||
"reviews.col.source": "Source",
|
||||
"reviews.col.target": "Translation",
|
||||
"reviews.col.status": "Status",
|
||||
"reviews.status.approved": "Approved",
|
||||
"reviews.status.edited": "Edited",
|
||||
"reviews.status.pending": "To review",
|
||||
"reviews.save": "Save",
|
||||
"reviews.cancel": "Cancel",
|
||||
"reviews.emptyCell": "— empty —",
|
||||
"reviews.action.edit": "Edit the translation",
|
||||
"reviews.action.approve": "Approve as is",
|
||||
"reviews.action.reset": "Set back to review",
|
||||
"reviews.approvedTitle": "Segments approved",
|
||||
"reviews.approvedDesc": "{count} segment(s) approved.",
|
||||
"reviews.rebuiltTitle": "Document rebuilt",
|
||||
"reviews.rebuiltDesc": "{count} reviewed segment(s) applied.",
|
||||
"reviews.rebuildFailed": "Rebuild failed",
|
||||
"reviews.xliffExportFailed": "XLIFF export failed",
|
||||
"reviews.xliffImported": "XLIFF imported",
|
||||
"reviews.xliffImportedDesc": "{count} segment(s) updated.",
|
||||
"reviews.xliffImportFailed": "XLIFF import failed",
|
||||
"reviews.error.title": "Error",
|
||||
"reviews.error.load": "Loading failed",
|
||||
"reviews.error.update": "Could not update",
|
||||
"reviews.error.download": "Download failed (HTTP {status})"
|
||||
}
|
||||
@@ -10,5 +10,6 @@
|
||||
"services.adminOnly.title": "Provider configuration is admin-only",
|
||||
"services.adminOnly.desc": "API keys, model selection, and provider activation are managed exclusively by the administrator in the admin panel. You never need to enter an API key.",
|
||||
"services.fallback.google.label": "Google Translate",
|
||||
"services.fallback.google.desc": "Fast translation, 130+ languages"
|
||||
"services.fallback.google.desc": "Fast translation, 130+ languages",
|
||||
"services.pill": "Engines"
|
||||
}
|
||||
|
||||
@@ -16,5 +16,8 @@
|
||||
"settings.cache.clear": "Clear cache",
|
||||
"settings.formats.excel.name": "Excel",
|
||||
"settings.formats.word.name": "Word",
|
||||
"settings.formats.powerpoint.name": "PowerPoint"
|
||||
"settings.formats.powerpoint.name": "PowerPoint",
|
||||
"settings.pill": "Configuration",
|
||||
"settings.cache.confirm": "Confirm and reload",
|
||||
"settings.cache.cancelClear": "Cancel"
|
||||
}
|
||||
|
||||
26
frontend/src/lib/i18n/messages/en/teams.json
Normal file
26
frontend/src/lib/i18n/messages/en/teams.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"teams.title": "Workspaces",
|
||||
"teams.subtitle": "Share your glossaries with your team (Business plan — 5 seats).",
|
||||
"teams.needsBusiness": "Workspaces require the Business plan. Upgrade to collaborate with your team.",
|
||||
"teams.newWorkspace": "New workspace",
|
||||
"teams.newPlaceholder": "e.g. Translation team",
|
||||
"teams.create": "Create",
|
||||
"teams.created": "Workspace created",
|
||||
"teams.createdDesc": "You are the owner.",
|
||||
"teams.createFailed": "Could not create",
|
||||
"teams.memberAdded": "Member added",
|
||||
"teams.addFailed": "Could not add",
|
||||
"teams.removeFailed": "Could not remove",
|
||||
"teams.removeMember": "Remove from workspace",
|
||||
"teams.owner": "Owner",
|
||||
"teams.seats": "{count} / {limit} seats",
|
||||
"teams.seatsUnlimited": "{count} seats (unlimited)",
|
||||
"teams.role.member": "Member",
|
||||
"teams.role.admin": "Admin",
|
||||
"teams.addMember": "Add a member (email)",
|
||||
"teams.emailPlaceholder": "colleague@company.com",
|
||||
"teams.invite": "Invite",
|
||||
"teams.empty": "No workspace yet — create the first one above.",
|
||||
"teams.error.title": "Error",
|
||||
"teams.error.load": "Could not load"
|
||||
}
|
||||
@@ -89,5 +89,25 @@
|
||||
"translate.preserveLayoutDesc": "Preserve the layout",
|
||||
"translate.textOnly": "Text only",
|
||||
"translate.textOnlyDesc": "Fast translation of text only",
|
||||
"translate.unavailableStandard": "Unavailable in Standard mode (AI only)"
|
||||
"translate.unavailableStandard": "Unavailable in Standard mode (AI only)",
|
||||
"translate.header.aiActiveTitle": "AI analysis",
|
||||
"translate.header.aiActiveAccent": "active",
|
||||
"translate.header.completedTitleBase": "Translation",
|
||||
"translate.header.completedTitleAccent": "completed",
|
||||
"translate.header.translateDocBase": "Translate",
|
||||
"translate.header.translateDocAccent": "a document",
|
||||
"translate.header.workspace": "Translation workspace",
|
||||
"translate.monitor.source": "Source",
|
||||
"translate.monitor.target": "Target",
|
||||
"translate.monitor.engine": "Engine",
|
||||
"translate.stat.progress": "progress",
|
||||
"translate.stat.remaining": "time left",
|
||||
"translate.stat.elapsed": "elapsed",
|
||||
"translate.leaveScreen": "Back to start",
|
||||
"translate.leaveScreenHint": "The translation keeps running in the background.",
|
||||
"translate.reviewCta": "Review segment by segment",
|
||||
"translate.downloadFailed": "The download failed. Please try again.",
|
||||
"translate.recent.title": "Recent translations",
|
||||
"translate.recent.review": "Review",
|
||||
"translate.upload.ariaDropzone": "Upload a document: drag and drop, or press Enter to browse files"
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Descubre Memento",
|
||||
"memento.slogan": "Memento no es solo una aplicación de notas. Es un ecosistema inteligente que conecta, analiza y desarrolla tus ideas en tiempo real usando 6 agentes de IA y búsqueda semántica avanzada.",
|
||||
"memento.ctaFree": "Empezar gratis",
|
||||
"memento.ctaMore": "Saber más",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -41,7 +40,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Memento را کشف کنید",
|
||||
"memento.slogan": "Memento فقط یک برنامه یادداشت نیست. یک اکوسیستم هوشمند است که با استفاده از ۶ عامل هوش مصنوعی و جستجوی معنایی پیشرفته، ایدههای شما را در زمان واقعی متصل، تحلیل و توسعه میدهد.",
|
||||
"memento.ctaFree": "رایگان شروع کنید",
|
||||
"memento.ctaMore": "بیشتر بدانید",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -112,5 +112,6 @@
|
||||
"dashboard.checkoutSyncError": "Erreur lors de la synchronisation du paiement.",
|
||||
"dashboard.networkRefresh": "Erreur réseau. Veuillez rafraîchir la page.",
|
||||
"dashboard.continueToTranslate": "Continuer vers la traduction",
|
||||
"dashboard.nav.teams": "Équipe"
|
||||
"dashboard.nav.teams": "Équipe",
|
||||
"dashboard.loading": "Chargement…"
|
||||
}
|
||||
|
||||
@@ -27,5 +27,7 @@
|
||||
"fileUploader.foundTexts": "{count} textes trouvés à traduire",
|
||||
"fileUploader.translatingItem": "Traduction {current}/{total} : « {preview} »",
|
||||
"fileUploader.reconstructing": "Reconstruction du document…",
|
||||
"fileUploader.translatingLocally": "Traduction locale avec WebLLM…"
|
||||
"fileUploader.translatingLocally": "Traduction locale avec WebLLM…",
|
||||
"fileUploader.error.invalidFormat": "Format non supporté. Formats acceptés : .xlsx, .docx, .pptx, .pdf",
|
||||
"fileUploader.error.tooLarge": "Fichier trop volumineux (max 50 Mo)"
|
||||
}
|
||||
|
||||
@@ -189,5 +189,36 @@
|
||||
"glossaries.detail.importedTitle": "Importé",
|
||||
"glossaries.detail.importedDesc": "{count} termes importés.",
|
||||
"glossaries.detail.importErrorTitle": "Erreur de lecture",
|
||||
"glossaries.detail.importErrorDesc": "Impossible de lire le fichier."
|
||||
"glossaries.detail.importErrorDesc": "Impossible de lire le fichier.",
|
||||
"glossaries.titleBase": "Glossaires &",
|
||||
"glossaries.titleAccent": "Contexte",
|
||||
"glossaries.new": "Nouveau glossaire",
|
||||
"glossaries.info.title": "Pour utiliser un glossaire dans une traduction :",
|
||||
"glossaries.info.body": "cliquez sur « Utiliser » sur la carte souhaitée. Vous serez redirigé sur la page Traduire avec ce glossaire déjà sélectionné.",
|
||||
"glossaries.info.cta": "Aller traduire",
|
||||
"glossaries.context.title": "Consignes de contexte",
|
||||
"glossaries.context.desc": "Ces instructions sont envoyées automatiquement à l'IA avant chaque traduction en mode Pro LLM.",
|
||||
"glossaries.context.unsaved": "Non sauvegardé",
|
||||
"glossaries.context.active": "Actif",
|
||||
"glossaries.context.inactive": "Inactif",
|
||||
"glossaries.context.suggestions": "Suggestions rapides — cliquez pour ajouter",
|
||||
"glossaries.context.placeholder": "Ex : Vous traduisez des documents techniques HVAC. Utilisez une terminologie d'ingénierie précise et un ton professionnel…",
|
||||
"glossaries.context.chars": "{count} caractères",
|
||||
"glossaries.context.noPrompt": "Aucune consigne enregistrée",
|
||||
"glossaries.context.clear": "Tout effacer",
|
||||
"glossaries.context.saving": "Enregistrement…",
|
||||
"glossaries.context.saved": "Enregistré",
|
||||
"glossaries.context.save": "Enregistrer",
|
||||
"glossaries.activeTarget": "Traduction active :",
|
||||
"glossaries.searchPlaceholder": "Rechercher un glossaire…",
|
||||
"glossaries.countMany": "{count} glossaires — cliquez sur une carte pour la modifier",
|
||||
"glossaries.countNone": "Aucun glossaire — créez-en un pour commencer",
|
||||
"glossaries.termsOne": "1 terme",
|
||||
"glossaries.termsMany": "{count} termes",
|
||||
"glossaries.badge.otherLang": "Autre langue",
|
||||
"glossaries.card.edit": "Éditer",
|
||||
"glossaries.card.use": "Utiliser",
|
||||
"glossaries.createFirst": "Créer mon premier glossaire",
|
||||
"glossaries.noResults": "Aucun résultat pour « {query} »",
|
||||
"glossaries.card.terms": "termes"
|
||||
}
|
||||
|
||||
@@ -16,15 +16,16 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
import providerTheme from "./providerTheme.json";
|
||||
import register from "./register.json";
|
||||
import resetPassword from "./resetPassword.json";
|
||||
import reviews from "./reviews.json";
|
||||
import services from "./services.json";
|
||||
import settings from "./settings.json";
|
||||
import teams from "./teams.json";
|
||||
import translate from "./translate.json";
|
||||
import translateComplete from "./translateComplete.json";
|
||||
|
||||
@@ -44,15 +45,16 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
...providerTheme,
|
||||
...register,
|
||||
...resetPassword,
|
||||
...reviews,
|
||||
...services,
|
||||
...settings,
|
||||
...teams,
|
||||
...translate,
|
||||
...translateComplete,
|
||||
};
|
||||
|
||||
@@ -148,5 +148,6 @@
|
||||
"landing.translate.supportedFormats": "Fichiers DOCX, XLSX, PPTX ou PDF supportés",
|
||||
"landing.translate.aiAnalysis": "Analyse IA Active",
|
||||
"landing.translate.processing": "Traitement en cours",
|
||||
"landing.translate.preservingLayout": "Votre mise en page est en cours de préservation"
|
||||
"landing.translate.preservingLayout": "Votre mise en page est en cours de préservation",
|
||||
"landing.formats.pill": "COMPATIBILITÉ"
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
"langSelector.noResults": "Aucun résultat",
|
||||
"langSelector.source": "Source",
|
||||
"langSelector.target": "Cible",
|
||||
"langSelector.swap": "Inverser"
|
||||
"langSelector.swap": "Inverser",
|
||||
"langSelector.changeLanguage": "Changer la langue"
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Découvrez Memento",
|
||||
"memento.slogan": "Memento n'est pas qu'une simple application de notes. C'est un écosystème intelligent qui connecte, analyse et développe vos idées en temps réel grâce à 6 types d'agents IA et une recherche sémantique de pointe.",
|
||||
"memento.ctaFree": "Commencer gratuitement",
|
||||
"memento.ctaMore": "Voir plus",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -147,5 +147,10 @@
|
||||
"pricing.toast.paymentError": "Erreur lors de la création du paiement.",
|
||||
"pricing.dashboard": "Tableau de bord",
|
||||
"pricing.okSymbol": "✓",
|
||||
"pricing.errSymbol": "✕"
|
||||
"pricing.errSymbol": "✕",
|
||||
"pricing.error.server": "Erreur serveur {status}",
|
||||
"pricing.enterprise.subject": "Offre Enterprise",
|
||||
"pricing.toast.close": "Fermer",
|
||||
"pricing.header.titleBase": "Un forfait pour",
|
||||
"pricing.header.titleAccent": "chaque besoin"
|
||||
}
|
||||
|
||||
32
frontend/src/lib/i18n/messages/fr/reviews.json
Normal file
32
frontend/src/lib/i18n/messages/fr/reviews.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"reviews.title": "Relecture",
|
||||
"reviews.subtitle": "{file} — {total} segments ({pending} à relire, {approved} approuvés, {edited} modifiés)",
|
||||
"reviews.approveAll": "Tout approuver",
|
||||
"reviews.importXliff": "Importer XLIFF",
|
||||
"reviews.rebuild": "Reconstruire et télécharger",
|
||||
"reviews.col.source": "Source",
|
||||
"reviews.col.target": "Traduction",
|
||||
"reviews.col.status": "Statut",
|
||||
"reviews.status.approved": "Approuvé",
|
||||
"reviews.status.edited": "Modifié",
|
||||
"reviews.status.pending": "À relire",
|
||||
"reviews.save": "Enregistrer",
|
||||
"reviews.cancel": "Annuler",
|
||||
"reviews.emptyCell": "— vide —",
|
||||
"reviews.action.edit": "Modifier la traduction",
|
||||
"reviews.action.approve": "Approuver tel quel",
|
||||
"reviews.action.reset": "Remettre à relire",
|
||||
"reviews.approvedTitle": "Segments approuvés",
|
||||
"reviews.approvedDesc": "{count} segment(s) approuvé(s).",
|
||||
"reviews.rebuiltTitle": "Document reconstruit",
|
||||
"reviews.rebuiltDesc": "{count} segment(s) relu(s) appliqué(s).",
|
||||
"reviews.rebuildFailed": "Reconstruction impossible",
|
||||
"reviews.xliffExportFailed": "Export XLIFF échoué",
|
||||
"reviews.xliffImported": "XLIFF importé",
|
||||
"reviews.xliffImportedDesc": "{count} segment(s) mis à jour.",
|
||||
"reviews.xliffImportFailed": "Import XLIFF échoué",
|
||||
"reviews.error.title": "Erreur",
|
||||
"reviews.error.load": "Erreur de chargement",
|
||||
"reviews.error.update": "Mise à jour impossible",
|
||||
"reviews.error.download": "Téléchargement échoué (HTTP {status})"
|
||||
}
|
||||
@@ -10,5 +10,6 @@
|
||||
"services.adminOnly.title": "La configuration des fournisseurs est réservée à l'administrateur",
|
||||
"services.adminOnly.desc": "Les clés API, la sélection de modèle et l'activation des fournisseurs sont gérées exclusivement par l'administrateur. Vous n'avez jamais besoin de saisir de clé API.",
|
||||
"services.fallback.google.label": "Google Traduction",
|
||||
"services.fallback.google.desc": "Traduction rapide, 130+ langues"
|
||||
"services.fallback.google.desc": "Traduction rapide, 130+ langues",
|
||||
"services.pill": "Moteurs"
|
||||
}
|
||||
|
||||
@@ -16,5 +16,8 @@
|
||||
"settings.cache.clear": "Effacer le cache",
|
||||
"settings.formats.excel.name": "Excel",
|
||||
"settings.formats.word.name": "Word",
|
||||
"settings.formats.powerpoint.name": "PowerPoint"
|
||||
"settings.formats.powerpoint.name": "PowerPoint",
|
||||
"settings.pill": "Configuration",
|
||||
"settings.cache.confirm": "Confirmer et recharger",
|
||||
"settings.cache.cancelClear": "Annuler"
|
||||
}
|
||||
|
||||
26
frontend/src/lib/i18n/messages/fr/teams.json
Normal file
26
frontend/src/lib/i18n/messages/fr/teams.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"teams.title": "Espaces de travail",
|
||||
"teams.subtitle": "Partagez vos glossaires avec votre équipe (plan Business — 5 sièges).",
|
||||
"teams.needsBusiness": "Les espaces de travail nécessitent le plan Business. Passez au plan supérieur pour collaborer avec votre équipe.",
|
||||
"teams.newWorkspace": "Nouvel espace de travail",
|
||||
"teams.newPlaceholder": "Ex. Équipe traduction",
|
||||
"teams.create": "Créer",
|
||||
"teams.created": "Espace créé",
|
||||
"teams.createdDesc": "Vous êtes propriétaire.",
|
||||
"teams.createFailed": "Création impossible",
|
||||
"teams.memberAdded": "Membre ajouté",
|
||||
"teams.addFailed": "Ajout impossible",
|
||||
"teams.removeFailed": "Retrait impossible",
|
||||
"teams.removeMember": "Retirer du workspace",
|
||||
"teams.owner": "Propriétaire",
|
||||
"teams.seats": "{count} / {limit} sièges",
|
||||
"teams.seatsUnlimited": "{count} sièges (illimité)",
|
||||
"teams.role.member": "Membre",
|
||||
"teams.role.admin": "Admin",
|
||||
"teams.addMember": "Ajouter un membre (e-mail)",
|
||||
"teams.emailPlaceholder": "collegue@entreprise.com",
|
||||
"teams.invite": "Inviter",
|
||||
"teams.empty": "Aucun espace pour l'instant — créez le premier ci-dessus.",
|
||||
"teams.error.title": "Erreur",
|
||||
"teams.error.load": "Chargement impossible"
|
||||
}
|
||||
@@ -89,5 +89,25 @@
|
||||
"translate.preserveLayoutDesc": "Conserver la mise en page",
|
||||
"translate.textOnly": "Texte brut",
|
||||
"translate.textOnlyDesc": "Traduction rapide du texte uniquement",
|
||||
"translate.unavailableStandard": "Indisponible en mode Standard (IA uniquement)"
|
||||
"translate.unavailableStandard": "Indisponible en mode Standard (IA uniquement)",
|
||||
"translate.header.aiActiveTitle": "Analyse IA",
|
||||
"translate.header.aiActiveAccent": "active",
|
||||
"translate.header.completedTitleBase": "Traduction",
|
||||
"translate.header.completedTitleAccent": "terminée",
|
||||
"translate.header.translateDocBase": "Traduire",
|
||||
"translate.header.translateDocAccent": "un document",
|
||||
"translate.header.workspace": "Espace de traduction",
|
||||
"translate.monitor.source": "Source",
|
||||
"translate.monitor.target": "Cible",
|
||||
"translate.monitor.engine": "Moteur",
|
||||
"translate.stat.progress": "progression",
|
||||
"translate.stat.remaining": "temps restant",
|
||||
"translate.stat.elapsed": "temps écoulé",
|
||||
"translate.leaveScreen": "Revenir à l'accueil",
|
||||
"translate.leaveScreenHint": "La traduction continue en arrière-plan.",
|
||||
"translate.reviewCta": "Relire segment par segment",
|
||||
"translate.downloadFailed": "Le téléchargement a échoué. Réessayez.",
|
||||
"translate.recent.title": "Traductions récentes",
|
||||
"translate.recent.review": "Relire",
|
||||
"translate.upload.ariaDropzone": "Déposer un document : glisser-déposer ou appuyer sur Entrée pour parcourir"
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Scopri Memento",
|
||||
"memento.slogan": "Memento non è solo un'app di note. È un ecosistema intelligente che connette, analizza e sviluppa le tue idee in tempo reale usando 6 agenti IA e ricerca semantica avanzata.",
|
||||
"memento.ctaFree": "Inizia gratis",
|
||||
"memento.ctaMore": "Scopri di più",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Mementoを発見",
|
||||
"memento.slogan": "Mementoは単なるメモアプリではありません。6つのAIエージェントと高度なセマンティック検索を使用して、アイデアをリアルタイムで接続、分析、発展させるインテリジェントなエコシステムです。",
|
||||
"memento.ctaFree": "無料で始める",
|
||||
"memento.ctaMore": "詳しく見る",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Memento 발견하기",
|
||||
"memento.slogan": "Memento는 단순한 메모 앱이 아닙니다. 6개의 AI 에이전트와 최첨단 시맨틱 검색을 사용하여 아이디어를 실시간으로 연결, 분석, 발전시키는 지능형 생태계입니다.",
|
||||
"memento.ctaFree": "무료로 시작",
|
||||
"memento.ctaMore": "자세히 보기",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Ontdek Memento",
|
||||
"memento.slogan": "Memento is meer dan alleen een notitie-app. Het is een intelligent ecosysteem dat uw ideeën in realtime verbindt, analyseert en verder ontwikkelt met 6 AI-agents en geavanceerde semantische zoekfuncties.",
|
||||
"memento.ctaFree": "Gratis beginnen",
|
||||
"memento.ctaMore": "Meer informatie",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Descubra o Memento",
|
||||
"memento.slogan": "O Memento é mais do que uma aplicação de notas. É um ecossistema inteligente que conecta, analisa e desenvolve suas ideias em tempo real usando 6 agentes de IA e busca semântica avançada.",
|
||||
"memento.ctaFree": "Começar grátis",
|
||||
"memento.ctaMore": "Saiba mais",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -44,7 +43,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "Откройте Memento",
|
||||
"memento.slogan": "Memento — это больше, чем приложение для заметок. Это интеллектуальная экосистема, которая связывает, анализирует и развивает ваши идеи в реальном времени с помощью 6 ИИ-агентов и продвинутого семантического поиска.",
|
||||
"memento.ctaFree": "Начать бесплатно",
|
||||
"memento.ctaMore": "Узнать больше",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
@@ -41,7 +40,6 @@ const messages: Record<string, string> = {
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"memento.title": "探索 Memento",
|
||||
"memento.slogan": "Memento 不仅仅是一个笔记应用。它是一个智能生态系统,利用 6 个 AI 代理和先进的语义搜索,实时连接、分析和发展您的想法。",
|
||||
"memento.ctaFree": "免费开始",
|
||||
"memento.ctaMore": "了解更多",
|
||||
"memento.url": "https://memento-note.com/"
|
||||
}
|
||||
Reference in New Issue
Block a user