feat(i18n): Phase 1 — migrate 5 critical files from hardcoded text to i18n
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
47 new i18n keys added across all 13 locales (en, fr, es, de, pt, it, nl, ru, ja, ko, zh, ar, fa). English and French are fully translated, remaining locales use French as placeholder. Files migrated: - EditGlossaryDialog.tsx (18 strings) - DeleteGlossaryDialog.tsx (7 strings) - ProUpgradePrompt.tsx (10 strings) - WebhookSnippet.tsx (4 strings) - TranslationModeToggle.tsx (8 strings) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { Webhook, Copy, Check } from 'lucide-react';
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { API_BASE_URL } from '@/lib/apiClient';
|
import { API_BASE_URL } from '@/lib/apiClient';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
|
||||||
function getWebhookSnippet(): string {
|
function getWebhookSnippet(): string {
|
||||||
const baseUrl = API_BASE_URL.replace(/\/$/, '');
|
const baseUrl = API_BASE_URL.replace(/\/$/, '');
|
||||||
@@ -18,6 +19,7 @@ function getWebhookSnippet(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function WebhookSnippet() {
|
export function WebhookSnippet() {
|
||||||
|
const { t } = useI18n();
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
const webhookSnippet = useMemo(() => getWebhookSnippet(), []);
|
const webhookSnippet = useMemo(() => getWebhookSnippet(), []);
|
||||||
@@ -33,13 +35,12 @@ export function WebhookSnippet() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Webhook className="h-5 w-5 text-accent" />
|
<Webhook className="h-5 w-5 text-accent" />
|
||||||
<CardTitle className="text-base">Webhook Integration</CardTitle>
|
<CardTitle className="text-base">{t('apiKeys.webhook.title')}</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Pass a <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">webhook_url</code> parameter
|
{t('apiKeys.webhook.descriptionBefore')}<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">{t('apiKeys.webhook.codeParam')}</code>{t('apiKeys.webhook.descriptionAfter')}
|
||||||
to receive a POST request when your translation is complete.
|
|
||||||
</p>
|
</p>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
|
||||||
interface DeleteGlossaryDialogProps {
|
interface DeleteGlossaryDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -26,13 +27,14 @@ export function DeleteGlossaryDialog({
|
|||||||
isDeleting,
|
isDeleting,
|
||||||
glossaryName,
|
glossaryName,
|
||||||
}: DeleteGlossaryDialogProps) {
|
}: DeleteGlossaryDialogProps) {
|
||||||
|
const { t } = useI18n();
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-md">
|
<DialogContent className="sm:max-w-md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Delete Glossary</DialogTitle>
|
<DialogTitle>{t('glossaries.delete.title')}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Are you sure you want to delete this glossary?
|
{t('glossaries.delete.description')}
|
||||||
{glossaryName && (
|
{glossaryName && (
|
||||||
<span className="block mt-1 font-medium text-foreground">
|
<span className="block mt-1 font-medium text-foreground">
|
||||||
"{glossaryName}"
|
"{glossaryName}"
|
||||||
@@ -45,9 +47,9 @@ export function DeleteGlossaryDialog({
|
|||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<AlertTriangle className="h-5 w-5 text-destructive shrink-0 mt-0.5" />
|
<AlertTriangle className="h-5 w-5 text-destructive shrink-0 mt-0.5" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-sm font-medium text-destructive">This action cannot be undone</p>
|
<p className="text-sm font-medium text-destructive">{t('glossaries.delete.warning')}</p>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
All term pairs will be permanently removed.
|
{t('glossaries.delete.warningDesc')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,14 +61,14 @@ export function DeleteGlossaryDialog({
|
|||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
disabled={isDeleting}
|
disabled={isDeleting}
|
||||||
>
|
>
|
||||||
Cancel
|
{t('glossaries.delete.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={onConfirm}
|
onClick={onConfirm}
|
||||||
disabled={isDeleting}
|
disabled={isDeleting}
|
||||||
>
|
>
|
||||||
{isDeleting ? 'Deleting...' : 'Delete'}
|
{isDeleting ? t('glossaries.delete.deleting') : t('glossaries.delete.deleteBtn')}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { exportGlossaryToCsv, parseCsvToTerms } from './csvUtils';
|
|||||||
import { useToast } from '@/components/ui/toast';
|
import { useToast } from '@/components/ui/toast';
|
||||||
import type { Glossary, GlossaryTermInput } from './types';
|
import type { Glossary, GlossaryTermInput } from './types';
|
||||||
import { MAX_TERMS_PER_GLOSSARY, SUPPORTED_LANGUAGES } from './types';
|
import { MAX_TERMS_PER_GLOSSARY, SUPPORTED_LANGUAGES } from './types';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
|
||||||
interface EditGlossaryDialogProps {
|
interface EditGlossaryDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -41,6 +42,7 @@ export function EditGlossaryDialog({
|
|||||||
onSave,
|
onSave,
|
||||||
isSaving,
|
isSaving,
|
||||||
}: EditGlossaryDialogProps) {
|
}: EditGlossaryDialogProps) {
|
||||||
|
const { t } = useI18n();
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [sourceLanguage, setSourceLanguage] = useState('fr');
|
const [sourceLanguage, setSourceLanguage] = useState('fr');
|
||||||
const [targetLanguage, setTargetLanguage] = useState('multi');
|
const [targetLanguage, setTargetLanguage] = useState('multi');
|
||||||
@@ -133,22 +135,22 @@ export function EditGlossaryDialog({
|
|||||||
if (importedTerms.length > MAX_TERMS_PER_GLOSSARY) {
|
if (importedTerms.length > MAX_TERMS_PER_GLOSSARY) {
|
||||||
toast({
|
toast({
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
title: 'Import failed',
|
title: t('glossaries.edit.importFailedTitle'),
|
||||||
description: `CSV contains ${importedTerms.length} terms, but maximum is ${MAX_TERMS_PER_GLOSSARY}. Please reduce the number of terms.`,
|
description: t('glossaries.edit.importFailedMaxDesc', { count: importedTerms.length, max: MAX_TERMS_PER_GLOSSARY }),
|
||||||
});
|
});
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setTerms(importedTerms);
|
setTerms(importedTerms);
|
||||||
toast({
|
toast({
|
||||||
title: 'Import successful',
|
title: t('glossaries.edit.importSuccessTitle'),
|
||||||
description: `${importedTerms.length} terms imported successfully.`,
|
description: t('glossaries.edit.importSuccessDesc', { count: importedTerms.length }),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
title: 'Import failed',
|
title: t('glossaries.edit.importFailedTitle'),
|
||||||
description: 'No valid terms found in CSV file.',
|
description: t('glossaries.edit.importFailedEmptyDesc'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,8 +158,8 @@ export function EditGlossaryDialog({
|
|||||||
reader.onerror = () => {
|
reader.onerror = () => {
|
||||||
toast({
|
toast({
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
title: 'Import failed',
|
title: t('glossaries.edit.importFailedTitle'),
|
||||||
description: 'Failed to read CSV file.',
|
description: t('glossaries.edit.importFailedReadDesc'),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
@@ -171,27 +173,27 @@ export function EditGlossaryDialog({
|
|||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Edit Glossary</DialogTitle>
|
<DialogTitle>{t('glossaries.edit.title')}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Update the glossary name, language pair, and terms.
|
{t('glossaries.edit.description')}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-6 py-4">
|
<div className="space-y-6 py-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="glossary-name">Glossary Name</Label>
|
<Label htmlFor="glossary-name">{t('glossaries.edit.nameLabel')}</Label>
|
||||||
<Input
|
<Input
|
||||||
id="glossary-name"
|
id="glossary-name"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
placeholder="Enter glossary name..."
|
placeholder={t('glossaries.edit.namePlaceholder')}
|
||||||
disabled={isSaving}
|
disabled={isSaving}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Label className="text-xs text-muted-foreground mb-1 block">Langue source</Label>
|
<Label className="text-xs text-muted-foreground mb-1 block">{t('glossaries.edit.sourceLang')}</Label>
|
||||||
<select
|
<select
|
||||||
value={sourceLanguage}
|
value={sourceLanguage}
|
||||||
onChange={e => setSourceLanguage(e.target.value)}
|
onChange={e => setSourceLanguage(e.target.value)}
|
||||||
@@ -205,7 +207,7 @@ export function EditGlossaryDialog({
|
|||||||
</div>
|
</div>
|
||||||
<div className="pt-5 text-muted-foreground font-bold">→</div>
|
<div className="pt-5 text-muted-foreground font-bold">→</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Label className="text-xs text-muted-foreground mb-1 block">Langue cible</Label>
|
<Label className="text-xs text-muted-foreground mb-1 block">{t('glossaries.edit.targetLang')}</Label>
|
||||||
<select
|
<select
|
||||||
value={targetLanguage}
|
value={targetLanguage}
|
||||||
onChange={e => handleTargetLanguageChange(e.target.value)}
|
onChange={e => handleTargetLanguageChange(e.target.value)}
|
||||||
@@ -220,7 +222,7 @@ export function EditGlossaryDialog({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Terms ({validTermsCount} valid)</Label>
|
<Label>{t('glossaries.edit.termsLabel', { count: validTermsCount })}</Label>
|
||||||
<TermEditor
|
<TermEditor
|
||||||
terms={terms}
|
terms={terms}
|
||||||
onChange={setTerms}
|
onChange={setTerms}
|
||||||
@@ -238,7 +240,7 @@ export function EditGlossaryDialog({
|
|||||||
className="gap-1.5"
|
className="gap-1.5"
|
||||||
>
|
>
|
||||||
<Download className="size-3.5" />
|
<Download className="size-3.5" />
|
||||||
Export CSV
|
{t('glossaries.edit.exportCsv')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -249,7 +251,7 @@ export function EditGlossaryDialog({
|
|||||||
className="gap-1.5"
|
className="gap-1.5"
|
||||||
>
|
>
|
||||||
<Upload className="size-3.5" />
|
<Upload className="size-3.5" />
|
||||||
Import CSV
|
{t('glossaries.edit.importCsv')}
|
||||||
</Button>
|
</Button>
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
@@ -267,13 +269,13 @@ export function EditGlossaryDialog({
|
|||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
disabled={isSaving}
|
disabled={isSaving}
|
||||||
>
|
>
|
||||||
Cancel
|
{t('glossaries.edit.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={isSaving || !name.trim()}
|
disabled={isSaving || !name.trim()}
|
||||||
>
|
>
|
||||||
{isSaving ? 'Saving...' : 'Save Changes'}
|
{isSaving ? t('glossaries.edit.saving') : t('glossaries.edit.saveChanges')}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import { BookText, Sparkles } from 'lucide-react';
|
|||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
|
||||||
export function ProUpgradePrompt() {
|
export function ProUpgradePrompt() {
|
||||||
|
const { t } = useI18n();
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-[60vh] p-6">
|
<div className="flex items-center justify-center min-h-[60vh] p-6">
|
||||||
<Card className="max-w-md w-full border-border/50 bg-gradient-to-br from-card via-card to-accent/5">
|
<Card className="max-w-md w-full border-border/50 bg-gradient-to-br from-card via-card to-accent/5">
|
||||||
@@ -13,39 +15,38 @@ export function ProUpgradePrompt() {
|
|||||||
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-accent/20 to-accent/5">
|
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-accent/20 to-accent/5">
|
||||||
<BookText className="h-8 w-8 text-accent" />
|
<BookText className="h-8 w-8 text-accent" />
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-2xl font-semibold">Glossaries</CardTitle>
|
<CardTitle className="text-2xl font-semibold">{t('glossaries.upgrade.title')}</CardTitle>
|
||||||
<CardDescription className="text-base">
|
<CardDescription className="text-base">
|
||||||
Customize your translations with custom terminology
|
{t('glossaries.upgrade.description')}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="text-center space-y-6">
|
<CardContent className="text-center space-y-6">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
||||||
<span>Create multiple glossaries</span>
|
<span>{t('glossaries.upgrade.feature1')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
||||||
<span>Define source→target term pairs</span>
|
<span>{t('glossaries.upgrade.feature2')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
||||||
<span>Import/export via CSV</span>
|
<span>{t('glossaries.upgrade.feature3')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
<Sparkles className="h-4 w-4 text-accent shrink-0" />
|
||||||
<span>Apply to LLM translations</span>
|
<span>{t('glossaries.upgrade.feature4')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-2">
|
<div className="pt-2">
|
||||||
<p className="text-sm text-muted-foreground mb-4">
|
<p className="text-sm text-muted-foreground mb-4">
|
||||||
Glossaries are a <span className="text-accent font-medium">Pro</span> feature.
|
{t('glossaries.upgrade.proFeatureBefore')}<span className="text-accent font-medium">{t('glossaries.upgrade.proLabel')}</span>{t('glossaries.upgrade.proFeatureAfter')}
|
||||||
Upgrade to unlock custom terminology.
|
|
||||||
</p>
|
</p>
|
||||||
<Button asChild className="w-full bg-accent hover:bg-accent/90">
|
<Button asChild className="w-full bg-accent hover:bg-accent/90">
|
||||||
<Link href="/pricing">
|
<Link href="/pricing">
|
||||||
Upgrade to Pro
|
{t('glossaries.upgrade.upgradeBtn')}
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@/components/ui/tooltip';
|
} from '@/components/ui/tooltip';
|
||||||
import type { TranslationMode } from './types';
|
import type { TranslationMode } from './types';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
|
||||||
interface TranslationModeToggleProps {
|
interface TranslationModeToggleProps {
|
||||||
mode: TranslationMode;
|
mode: TranslationMode;
|
||||||
@@ -21,11 +22,12 @@ export function TranslationModeToggle({
|
|||||||
onModeChange,
|
onModeChange,
|
||||||
isPro,
|
isPro,
|
||||||
}: TranslationModeToggleProps) {
|
}: TranslationModeToggleProps) {
|
||||||
|
const { t } = useI18n();
|
||||||
return (
|
return (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<label className="text-xs font-medium text-muted-foreground">
|
<label className="text-xs font-medium text-muted-foreground">
|
||||||
Translation Mode
|
{t('translate.mode.label')}
|
||||||
</label>
|
</label>
|
||||||
<div className="flex rounded-lg border border-border bg-muted p-1">
|
<div className="flex rounded-lg border border-border bg-muted p-1">
|
||||||
<button
|
<button
|
||||||
@@ -38,9 +40,9 @@ export function TranslationModeToggle({
|
|||||||
)}
|
)}
|
||||||
onClick={() => onModeChange('classic')}
|
onClick={() => onModeChange('classic')}
|
||||||
>
|
>
|
||||||
Classic
|
{t('translate.mode.classic')}
|
||||||
<span className="ms-1.5 text-xs text-muted-foreground">
|
<span className="ms-1.5 text-xs text-muted-foreground">
|
||||||
Fast
|
{t('translate.mode.classicDesc')}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@@ -57,9 +59,9 @@ export function TranslationModeToggle({
|
|||||||
onClick={() => isPro && onModeChange('llm')}
|
onClick={() => isPro && onModeChange('llm')}
|
||||||
disabled={!isPro}
|
disabled={!isPro}
|
||||||
>
|
>
|
||||||
Pro LLM
|
{t('translate.mode.proLlm')}
|
||||||
<span className="ms-1.5 text-xs text-muted-foreground">
|
<span className="ms-1.5 text-xs text-muted-foreground">
|
||||||
Context-Aware
|
{t('translate.mode.proLlmDesc')}
|
||||||
</span>
|
</span>
|
||||||
{!isPro && (
|
{!isPro && (
|
||||||
<Lock className="absolute right-2 top-1/2 -translate-y-1/2 size-3 text-muted-foreground" />
|
<Lock className="absolute right-2 top-1/2 -translate-y-1/2 size-3 text-muted-foreground" />
|
||||||
@@ -68,7 +70,7 @@ export function TranslationModeToggle({
|
|||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
{!isPro && (
|
{!isPro && (
|
||||||
<TooltipContent side="top">
|
<TooltipContent side="top">
|
||||||
<p>Upgrade to Pro for LLM translation</p>
|
<p>{t('translate.mode.tooltip')}</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -76,9 +78,9 @@ export function TranslationModeToggle({
|
|||||||
{!isPro && (
|
{!isPro && (
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
<a href="/pricing" className="text-primary hover:underline">
|
<a href="/pricing" className="text-primary hover:underline">
|
||||||
Upgrade to Pro
|
{t('translate.mode.upgradeLink')}
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
for LLM-powered translations
|
{t('translate.mode.upgradeDesc')}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -323,6 +323,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "terms imported",
|
"glossaries.dialog.termsImported": "terms imported",
|
||||||
"glossaries.dialog.changeFile": "Change file",
|
"glossaries.dialog.changeFile": "Change file",
|
||||||
"glossaries.dialog.retry": "Retry",
|
"glossaries.dialog.retry": "Retry",
|
||||||
|
"glossaries.edit.title": "Edit Glossary",
|
||||||
|
"glossaries.edit.description": "Update the glossary name, language pair, and terms.",
|
||||||
|
"glossaries.edit.nameLabel": "Glossary Name",
|
||||||
|
"glossaries.edit.namePlaceholder": "Enter glossary name...",
|
||||||
|
"glossaries.edit.sourceLang": "Source language",
|
||||||
|
"glossaries.edit.targetLang": "Target language",
|
||||||
|
"glossaries.edit.termsLabel": "Terms ({count} valid)",
|
||||||
|
"glossaries.edit.exportCsv": "Export CSV",
|
||||||
|
"glossaries.edit.importCsv": "Import CSV",
|
||||||
|
"glossaries.edit.cancel": "Cancel",
|
||||||
|
"glossaries.edit.saving": "Saving...",
|
||||||
|
"glossaries.edit.saveChanges": "Save Changes",
|
||||||
|
"glossaries.edit.importFailedTitle": "Import failed",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "CSV contains {count} terms, but maximum is {max}. Please reduce the number of terms.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Import successful",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} terms imported successfully.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "No valid terms found in CSV file.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Failed to read CSV file.",
|
||||||
|
"glossaries.delete.title": "Delete Glossary",
|
||||||
|
"glossaries.delete.description": "Are you sure you want to delete this glossary?",
|
||||||
|
"glossaries.delete.warning": "This action cannot be undone",
|
||||||
|
"glossaries.delete.warningDesc": "All term pairs will be permanently removed.",
|
||||||
|
"glossaries.delete.cancel": "Cancel",
|
||||||
|
"glossaries.delete.deleting": "Deleting...",
|
||||||
|
"glossaries.delete.deleteBtn": "Delete",
|
||||||
|
"glossaries.upgrade.title": "Glossaries",
|
||||||
|
"glossaries.upgrade.description": "Customize your translations with custom terminology",
|
||||||
|
"glossaries.upgrade.feature1": "Create multiple glossaries",
|
||||||
|
"glossaries.upgrade.feature2": "Define source→target term pairs",
|
||||||
|
"glossaries.upgrade.feature3": "Import/export via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Apply to LLM translations",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Glossaries are a ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": " feature. Upgrade to unlock custom terminology.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Upgrade to Pro",
|
||||||
|
"apiKeys.webhook.title": "Webhook Integration",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Pass a ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " parameter to receive a POST request when your translation is complete.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Translation Mode",
|
||||||
|
"translate.mode.classic": "Classic",
|
||||||
|
"translate.mode.classicDesc": "Fast",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Context-Aware",
|
||||||
|
"translate.mode.tooltip": "Upgrade to Pro for LLM translation",
|
||||||
|
"translate.mode.upgradeLink": "Upgrade to Pro",
|
||||||
|
"translate.mode.upgradeDesc": "for LLM-powered translations",
|
||||||
|
|
||||||
// ── Pricing page ──
|
// ── Pricing page ──
|
||||||
"pricing.nav.back": "Back",
|
"pricing.nav.back": "Back",
|
||||||
@@ -1140,6 +1187,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "termes importés",
|
"glossaries.dialog.termsImported": "termes importés",
|
||||||
"glossaries.dialog.changeFile": "Changer le fichier",
|
"glossaries.dialog.changeFile": "Changer le fichier",
|
||||||
"glossaries.dialog.retry": "Réessayer",
|
"glossaries.dialog.retry": "Réessayer",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
|
|
||||||
// ── Pricing page ──
|
// ── Pricing page ──
|
||||||
"pricing.nav.back": "Retour",
|
"pricing.nav.back": "Retour",
|
||||||
@@ -1943,6 +2037,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "términos importados",
|
"glossaries.dialog.termsImported": "términos importados",
|
||||||
"glossaries.dialog.changeFile": "Cambiar archivo",
|
"glossaries.dialog.changeFile": "Cambiar archivo",
|
||||||
"glossaries.dialog.retry": "Reintentar",
|
"glossaries.dialog.retry": "Reintentar",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "Volver",
|
"pricing.nav.back": "Volver",
|
||||||
"pricing.nav.home": "Inicio",
|
"pricing.nav.home": "Inicio",
|
||||||
"pricing.nav.mySubscription": "Mi suscripción",
|
"pricing.nav.mySubscription": "Mi suscripción",
|
||||||
@@ -2701,6 +2842,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "Begriffe importiert",
|
"glossaries.dialog.termsImported": "Begriffe importiert",
|
||||||
"glossaries.dialog.changeFile": "Datei ändern",
|
"glossaries.dialog.changeFile": "Datei ändern",
|
||||||
"glossaries.dialog.retry": "Erneut versuchen",
|
"glossaries.dialog.retry": "Erneut versuchen",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "Zurück",
|
"pricing.nav.back": "Zurück",
|
||||||
"pricing.nav.home": "Startseite",
|
"pricing.nav.home": "Startseite",
|
||||||
"pricing.nav.mySubscription": "Mein Abonnement",
|
"pricing.nav.mySubscription": "Mein Abonnement",
|
||||||
@@ -3459,6 +3647,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "termos importados",
|
"glossaries.dialog.termsImported": "termos importados",
|
||||||
"glossaries.dialog.changeFile": "Trocar arquivo",
|
"glossaries.dialog.changeFile": "Trocar arquivo",
|
||||||
"glossaries.dialog.retry": "Tentar novamente",
|
"glossaries.dialog.retry": "Tentar novamente",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "Voltar",
|
"pricing.nav.back": "Voltar",
|
||||||
"pricing.nav.home": "Início",
|
"pricing.nav.home": "Início",
|
||||||
"pricing.nav.mySubscription": "Minha assinatura",
|
"pricing.nav.mySubscription": "Minha assinatura",
|
||||||
@@ -4217,6 +4452,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "termini importati",
|
"glossaries.dialog.termsImported": "termini importati",
|
||||||
"glossaries.dialog.changeFile": "Cambia file",
|
"glossaries.dialog.changeFile": "Cambia file",
|
||||||
"glossaries.dialog.retry": "Riprova",
|
"glossaries.dialog.retry": "Riprova",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "Indietro",
|
"pricing.nav.back": "Indietro",
|
||||||
"pricing.nav.home": "Home",
|
"pricing.nav.home": "Home",
|
||||||
"pricing.nav.mySubscription": "Il mio abbonamento",
|
"pricing.nav.mySubscription": "Il mio abbonamento",
|
||||||
@@ -4975,6 +5257,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "termen geïmporteerd",
|
"glossaries.dialog.termsImported": "termen geïmporteerd",
|
||||||
"glossaries.dialog.changeFile": "Bestand wijzigen",
|
"glossaries.dialog.changeFile": "Bestand wijzigen",
|
||||||
"glossaries.dialog.retry": "Opnieuw proberen",
|
"glossaries.dialog.retry": "Opnieuw proberen",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "Terug",
|
"pricing.nav.back": "Terug",
|
||||||
"pricing.nav.home": "Home",
|
"pricing.nav.home": "Home",
|
||||||
"pricing.nav.mySubscription": "Mijn abonnement",
|
"pricing.nav.mySubscription": "Mijn abonnement",
|
||||||
@@ -5733,6 +6062,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "терминов импортировано",
|
"glossaries.dialog.termsImported": "терминов импортировано",
|
||||||
"glossaries.dialog.changeFile": "Изменить файл",
|
"glossaries.dialog.changeFile": "Изменить файл",
|
||||||
"glossaries.dialog.retry": "Повторить",
|
"glossaries.dialog.retry": "Повторить",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "Назад",
|
"pricing.nav.back": "Назад",
|
||||||
"pricing.nav.home": "Главная",
|
"pricing.nav.home": "Главная",
|
||||||
"pricing.nav.mySubscription": "Моя подписка",
|
"pricing.nav.mySubscription": "Моя подписка",
|
||||||
@@ -6493,6 +6869,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "件インポート済み",
|
"glossaries.dialog.termsImported": "件インポート済み",
|
||||||
"glossaries.dialog.changeFile": "ファイルを変更",
|
"glossaries.dialog.changeFile": "ファイルを変更",
|
||||||
"glossaries.dialog.retry": "再試行",
|
"glossaries.dialog.retry": "再試行",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "戻る",
|
"pricing.nav.back": "戻る",
|
||||||
"pricing.nav.home": "ホーム",
|
"pricing.nav.home": "ホーム",
|
||||||
"pricing.nav.mySubscription": "マイサブスクリプション",
|
"pricing.nav.mySubscription": "マイサブスクリプション",
|
||||||
@@ -7250,6 +7673,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "개 용어 가져옴",
|
"glossaries.dialog.termsImported": "개 용어 가져옴",
|
||||||
"glossaries.dialog.changeFile": "파일 변경",
|
"glossaries.dialog.changeFile": "파일 변경",
|
||||||
"glossaries.dialog.retry": "다시 시도",
|
"glossaries.dialog.retry": "다시 시도",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "뒤로",
|
"pricing.nav.back": "뒤로",
|
||||||
"pricing.nav.home": "홈",
|
"pricing.nav.home": "홈",
|
||||||
"pricing.nav.mySubscription": "내 구독",
|
"pricing.nav.mySubscription": "내 구독",
|
||||||
@@ -8007,6 +8477,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "个术语已导入",
|
"glossaries.dialog.termsImported": "个术语已导入",
|
||||||
"glossaries.dialog.changeFile": "更换文件",
|
"glossaries.dialog.changeFile": "更换文件",
|
||||||
"glossaries.dialog.retry": "重试",
|
"glossaries.dialog.retry": "重试",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "返回",
|
"pricing.nav.back": "返回",
|
||||||
"pricing.nav.home": "首页",
|
"pricing.nav.home": "首页",
|
||||||
"pricing.nav.mySubscription": "我的订阅",
|
"pricing.nav.mySubscription": "我的订阅",
|
||||||
@@ -8722,6 +9239,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "مصطلحات مستوردة",
|
"glossaries.dialog.termsImported": "مصطلحات مستوردة",
|
||||||
"glossaries.dialog.changeFile": "تغيير الملف",
|
"glossaries.dialog.changeFile": "تغيير الملف",
|
||||||
"glossaries.dialog.retry": "إعادة المحاولة",
|
"glossaries.dialog.retry": "إعادة المحاولة",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
"pricing.nav.back": "رجوع",
|
"pricing.nav.back": "رجوع",
|
||||||
"pricing.nav.home": "الرئيسية",
|
"pricing.nav.home": "الرئيسية",
|
||||||
"pricing.nav.mySubscription": "اشتراكي",
|
"pricing.nav.mySubscription": "اشتراكي",
|
||||||
@@ -9446,6 +10010,53 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
"glossaries.dialog.termsImported": "اصطلاح وارد شد",
|
"glossaries.dialog.termsImported": "اصطلاح وارد شد",
|
||||||
"glossaries.dialog.changeFile": "تغییر فایل",
|
"glossaries.dialog.changeFile": "تغییر فایل",
|
||||||
"glossaries.dialog.retry": "تلاش مجدد",
|
"glossaries.dialog.retry": "تلاش مجدد",
|
||||||
|
"glossaries.edit.title": "Modifier le glossaire",
|
||||||
|
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||||
|
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||||
|
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||||
|
"glossaries.edit.sourceLang": "Langue source",
|
||||||
|
"glossaries.edit.targetLang": "Langue cible",
|
||||||
|
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||||
|
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||||
|
"glossaries.edit.importCsv": "Importer CSV",
|
||||||
|
"glossaries.edit.cancel": "Annuler",
|
||||||
|
"glossaries.edit.saving": "Enregistrement...",
|
||||||
|
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||||
|
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||||
|
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||||
|
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||||
|
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||||
|
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||||
|
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||||
|
"glossaries.delete.title": "Supprimer le glossaire",
|
||||||
|
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||||
|
"glossaries.delete.warning": "Cette action est irréversible",
|
||||||
|
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||||
|
"glossaries.delete.cancel": "Annuler",
|
||||||
|
"glossaries.delete.deleting": "Suppression...",
|
||||||
|
"glossaries.delete.deleteBtn": "Supprimer",
|
||||||
|
"glossaries.upgrade.title": "Glossaires",
|
||||||
|
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||||
|
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||||
|
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||||
|
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||||
|
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||||
|
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||||
|
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||||
|
"glossaries.upgrade.proLabel": "Pro",
|
||||||
|
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||||
|
"apiKeys.webhook.title": "Intégration Webhook",
|
||||||
|
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||||
|
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||||
|
"apiKeys.webhook.codeParam": "webhook_url",
|
||||||
|
"translate.mode.label": "Mode de traduction",
|
||||||
|
"translate.mode.classic": "Classique",
|
||||||
|
"translate.mode.classicDesc": "Rapide",
|
||||||
|
"translate.mode.proLlm": "Pro LLM",
|
||||||
|
"translate.mode.proLlmDesc": "Contextuel",
|
||||||
|
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||||
|
"translate.mode.upgradeLink": "Passer à Pro",
|
||||||
|
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||||
|
|
||||||
// ── Pricing page ──
|
// ── Pricing page ──
|
||||||
"pricing.nav.back": "بازگشت",
|
"pricing.nav.back": "بازگشت",
|
||||||
|
|||||||
Reference in New Issue
Block a user