feat: merge Context page into Glossaries — single page for glossary + system prompt + presets
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m30s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m30s
- Add system prompt textarea and professional presets (HVAC, IT, Legal, etc.) to Glossaries page - Remove Context from sidebar navigation (constants.ts) - Make GlossarySelector always visible for Pro users (not just LLM mode) - Send system prompt from Zustand store to backend via custom_prompt - Add 24 new i18n keys across all 13 locales for glossaries page Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ export interface NavItem {
|
||||
export const baseNavItems: NavItem[] = [
|
||||
{ labelKey: 'dashboard.nav.translate', href: '/dashboard/translate', icon: FileText },
|
||||
{ labelKey: 'dashboard.nav.profile', href: '/dashboard/profile', icon: User },
|
||||
{ labelKey: 'dashboard.nav.context', href: '/dashboard/context', icon: Globe, proOnly: true },
|
||||
{ labelKey: 'dashboard.nav.glossaries', href: '/dashboard/glossaries', icon: BookText, proOnly: true },
|
||||
{ labelKey: 'dashboard.nav.apiKeys', href: '/dashboard/api-keys', icon: Key, proOnly: true },
|
||||
];
|
||||
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { BookText, Plus, Library, Calendar, Hash } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
BookText, Plus, Library, Calendar, Hash,
|
||||
Zap, Save, Trash2, MessageSquare, Loader2,
|
||||
Wrench, HardHat, Monitor, Scale, Stethoscope, BarChart3,
|
||||
Megaphone, Car,
|
||||
} from 'lucide-react';
|
||||
import { useUser } from '@/app/dashboard/useUser';
|
||||
import { useI18n, formatDate } from '@/lib/i18n';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { useGlossaries, useGlossary } from './useGlossaries';
|
||||
import type { Glossary, GlossaryTermInput, GlossaryListItem } from './types';
|
||||
import { ProUpgradePrompt } from './ProUpgradePrompt';
|
||||
import { GlossaryCard } from './GlossaryCard';
|
||||
import { CreateGlossaryDialog } from './CreateGlossaryDialog';
|
||||
import { EditGlossaryDialog } from './EditGlossaryDialog';
|
||||
import { DeleteGlossaryDialog } from './DeleteGlossaryDialog';
|
||||
import { useToast } from '@/components/ui/toast';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useTranslationStore } from '@/lib/store';
|
||||
import { API_BASE } from '@/lib/config';
|
||||
|
||||
const PRESETS = [
|
||||
{ key: 'hvac', title: 'HVAC / Génie climatique', desc: 'Thermique, ventilation, climatisation', icon: Wrench, templateId: 'hvac' },
|
||||
{ key: 'construction', title: 'BTP / Construction', desc: 'Gros œuvre, second œuvre, normes', icon: HardHat, templateId: 'construction' },
|
||||
{ key: 'it', title: 'IT / Logiciel', desc: 'Développement, infrastructure, DevOps', icon: Monitor, templateId: 'technology' },
|
||||
{ key: 'legal', title: 'Juridique / Contrats', desc: 'Droit des affaires, contentieux', icon: Scale, templateId: 'legal' },
|
||||
{ key: 'medical', title: 'Médical / Santé', desc: 'Pharmacologie, chirurgie, diagnostic', icon: Stethoscope, templateId: 'medical' },
|
||||
{ key: 'finance', title: 'Finance / Comptabilité', desc: 'IFRS, bilans, fiscalité', icon: BarChart3, templateId: 'finance' },
|
||||
{ key: 'marketing', title: 'Marketing / Publicité', desc: 'Digital, branding, analytics', icon: Megaphone, templateId: 'marketing' },
|
||||
{ key: 'automotive', title: 'Automobile', desc: 'Motorisation, ADAS, homologation', icon: Car, templateId: 'automotive' },
|
||||
];
|
||||
|
||||
export default function GlossariesPage() {
|
||||
const { t } = useI18n();
|
||||
@@ -31,6 +47,7 @@ export default function GlossariesPage() {
|
||||
importTemplate,
|
||||
} = useGlossaries();
|
||||
const { toast } = useToast();
|
||||
const { settings, updateSettings } = useTranslationStore();
|
||||
|
||||
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
||||
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
||||
@@ -38,6 +55,9 @@ export default function GlossariesPage() {
|
||||
const [selectedGlossary, setSelectedGlossary] = useState<GlossaryListItem | null>(null);
|
||||
const [glossaryToEdit, setGlossaryToEdit] = useState<Glossary | null>(null);
|
||||
const [glossaryToDelete, setGlossaryToDelete] = useState<{ id: string; name: string } | null>(null);
|
||||
const [systemPrompt, setSystemPrompt] = useState(settings.systemPrompt);
|
||||
const [isSavingPrompt, setIsSavingPrompt] = useState(false);
|
||||
const [creatingPreset, setCreatingPreset] = useState<string | null>(null);
|
||||
|
||||
const { glossary: fullGlossary, isLoading: isLoadingGlossaryDetail } = useGlossary(
|
||||
selectedGlossary?.id || null
|
||||
@@ -46,6 +66,58 @@ export default function GlossariesPage() {
|
||||
const isPro = user?.tier === 'pro';
|
||||
const isLoading = isLoadingUser || isLoadingGlossaries;
|
||||
|
||||
useEffect(() => {
|
||||
setSystemPrompt(settings.systemPrompt);
|
||||
}, [settings]);
|
||||
|
||||
const handleSavePrompt = async () => {
|
||||
setIsSavingPrompt(true);
|
||||
try {
|
||||
updateSettings({ systemPrompt });
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
toast({ title: t('context.saved'), description: t('context.savedDesc') });
|
||||
} finally { setIsSavingPrompt(false); }
|
||||
};
|
||||
|
||||
const handleClearPrompt = () => {
|
||||
updateSettings({ systemPrompt: '' });
|
||||
setSystemPrompt('');
|
||||
};
|
||||
|
||||
const handleCreatePresetGlossary = async (preset: typeof PRESETS[0]) => {
|
||||
setCreatingPreset(preset.key);
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
const params = new URLSearchParams({ template_id: preset.templateId });
|
||||
const res = await fetch(`${API_BASE}/api/v1/glossaries/import?${params.toString()}`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
});
|
||||
if (res.ok) {
|
||||
const result = await res.json();
|
||||
const glossary = result.data;
|
||||
toast({
|
||||
title: t('context.presets.created'),
|
||||
description: t('context.presets.createdDesc', {
|
||||
name: glossary?.name ?? preset.title,
|
||||
count: String(glossary?.terms?.length ?? 0),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
toast({ variant: 'destructive', title: t('glossaries.toast.error'), description: t('glossaries.toast.errorCreate') });
|
||||
}
|
||||
} catch {
|
||||
toast({ variant: 'destructive', title: t('glossaries.toast.error'), description: t('glossaries.toast.errorImport') });
|
||||
} finally {
|
||||
setCreatingPreset(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditClick = (id: string) => {
|
||||
const glossary = glossaries.find((g: GlossaryListItem) => g.id === id);
|
||||
if (glossary) {
|
||||
@@ -171,7 +243,81 @@ export default function GlossariesPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* ── Glossary Grid ──────────────────────────────────────── */}
|
||||
<div className="space-y-12">
|
||||
{/* ── System Prompt ────────────────────────────────────── */}
|
||||
<section className="editorial-card p-10 lg:p-12 bg-white dark:bg-[#141414] border-none shadow-editorial">
|
||||
<div className="flex items-center gap-4 mb-8 text-brand-accent">
|
||||
<MessageSquare size={20} />
|
||||
<h3 className="text-[11px] font-black uppercase tracking-[0.3em] text-brand-dark dark:text-white">
|
||||
{t('context.instructions.title')}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-sm text-brand-dark/40 dark:text-white/40 mb-10 font-medium">{t('context.instructions.desc')}</p>
|
||||
<textarea
|
||||
value={systemPrompt}
|
||||
onChange={e => setSystemPrompt(e.target.value)}
|
||||
placeholder={t('context.instructions.placeholder')}
|
||||
className="w-full h-48 p-8 bg-brand-muted dark:bg-white/5 rounded-[32px] border border-black/5 dark:border-white/10 text-sm focus:ring-2 focus:ring-brand-accent/20 focus:border-brand-accent/30 transition-all outline-none resize-y"
|
||||
/>
|
||||
<div className="flex justify-end mt-6 gap-4">
|
||||
<button
|
||||
onClick={handleClearPrompt}
|
||||
className="px-8 py-3 bg-brand-muted dark:bg-white/5 text-brand-dark/40 dark:text-white/40 rounded-xl text-[9px] font-black uppercase tracking-widest hover:text-brand-dark dark:hover:text-white transition-all"
|
||||
>
|
||||
<Trash2 size={12} className="inline mr-2" />{t('context.clearAll')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSavePrompt}
|
||||
disabled={isSavingPrompt}
|
||||
className="premium-button px-10 py-3 text-[9px] uppercase tracking-widest !rounded-xl flex items-center gap-3 disabled:opacity-50"
|
||||
>
|
||||
{isSavingPrompt ? <Loader2 size={14} className="animate-spin" /> : <Save size={14} />}
|
||||
{isSavingPrompt ? t('context.saving') : t('context.save')}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Professional Presets ─────────────────────────────── */}
|
||||
<section className="editorial-card p-10 lg:p-12 bg-white dark:bg-[#141414] border-none shadow-editorial">
|
||||
<div className="flex items-center gap-4 mb-8 text-brand-accent">
|
||||
<Zap size={20} />
|
||||
<h3 className="text-[11px] font-black uppercase tracking-[0.3em] text-brand-dark dark:text-white">
|
||||
{t('context.presets.title')}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-sm text-brand-dark/40 dark:text-white/40 mb-12 font-medium">{t('context.presets.desc')}</p>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{PRESETS.map((p) => {
|
||||
const Icon = p.icon;
|
||||
const isCreating = creatingPreset === p.key;
|
||||
return (
|
||||
<button
|
||||
key={p.key}
|
||||
onClick={() => handleCreatePresetGlossary(p)}
|
||||
disabled={!!creatingPreset}
|
||||
className="p-6 bg-brand-muted dark:bg-white/5 hover:bg-brand-dark dark:hover:bg-brand-dark group transition-all rounded-[32px] text-center border border-black/5 dark:border-white/10 hover:shadow-2xl hover:-translate-y-1 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<div className="flex justify-center mb-4 text-brand-dark group-hover:text-brand-accent group-hover:scale-125 transition-all">
|
||||
{isCreating ? <Loader2 size={24} className="animate-spin" /> : <Icon size={24} />}
|
||||
</div>
|
||||
<h4 className="text-[11px] font-black uppercase tracking-tight text-brand-dark dark:text-white group-hover:text-white mb-2">
|
||||
{p.title}
|
||||
</h4>
|
||||
<p className="text-[8px] text-brand-dark/30 dark:text-white/30 group-hover:text-white/40 font-bold uppercase tracking-widest leading-relaxed">
|
||||
{p.desc}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<p className="mt-8 text-[9px] text-brand-dark/20 dark:text-white/20 font-black uppercase tracking-widest italic border-t border-black/5 dark:border-white/5 pt-6">
|
||||
{t('context.presets.hint')}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* ── Glossary Grid ──────────────────────────────────────── */}
|
||||
{glossaries.length === 0 ? (
|
||||
<div className="editorial-card p-16 bg-white dark:bg-[#141414] border-none shadow-editorial text-center">
|
||||
<div className="w-16 h-16 bg-brand-muted dark:bg-white/10 rounded-2xl flex items-center justify-center text-brand-accent mx-auto mb-6">
|
||||
@@ -224,7 +370,7 @@ export default function GlossariesPage() {
|
||||
)}
|
||||
|
||||
{/* ── About section ──────────────────────────────────────── */}
|
||||
<div className="editorial-card p-10 lg:p-12 bg-white dark:bg-[#141414] border-none shadow-editorial mt-12">
|
||||
<div className="editorial-card p-10 lg:p-12 bg-white dark:bg-[#141414] border-none shadow-editorial">
|
||||
<div className="flex items-center gap-4 text-brand-accent mb-8">
|
||||
<BookText size={20} />
|
||||
<span className="text-[11px] font-black uppercase tracking-[0.3em] text-brand-dark dark:text-white">
|
||||
@@ -236,6 +382,7 @@ export default function GlossariesPage() {
|
||||
{t('glossaries.aboutFormat')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dialogs */}
|
||||
<CreateGlossaryDialog
|
||||
|
||||
@@ -402,7 +402,7 @@ export default function TranslatePage() {
|
||||
isPro={config.isPro}
|
||||
/>
|
||||
|
||||
{config.isPro && config.mode === 'llm' && (
|
||||
{config.isPro && (
|
||||
<GlossarySelector
|
||||
glossaryId={config.glossaryId}
|
||||
onChange={config.setGlossaryId}
|
||||
|
||||
@@ -147,6 +147,11 @@ export function useTranslationSubmit(): UseTranslationSubmitReturn {
|
||||
if (config.glossaryId) {
|
||||
formData.append('glossary_id', config.glossaryId);
|
||||
}
|
||||
// System prompt from Context page (Pro only)
|
||||
const { settings } = await import('@/lib/store').then(m => m.useTranslationStore.getState());
|
||||
if (settings.systemPrompt?.trim()) {
|
||||
formData.append('custom_prompt', settings.systemPrompt.trim());
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
@@ -244,6 +244,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Rebuild",
|
||||
"dashboard.translate.pipeline.finalize": "Finalize",
|
||||
"dashboard.translate.progress.failedTitle": "Translation failed",
|
||||
"glossaries.yourGlossaries": "Your glossaries",
|
||||
"glossaries.title": "Glossaries & Context",
|
||||
"glossaries.description": "Manage your glossaries and context instructions for more accurate translations.",
|
||||
"glossaries.createNew": "Create new",
|
||||
"glossaries.empty": "No glossaries yet",
|
||||
"glossaries.emptyDesc": "Create your first glossary or load a professional preset above",
|
||||
"glossaries.defineTerms": "terms",
|
||||
"glossaries.aboutTitle": "About glossaries",
|
||||
"glossaries.aboutDesc": "Glossaries let you define exact translations for specific terms. When you translate a document, the glossary terms are used to ensure consistent and accurate translations.",
|
||||
"glossaries.aboutFormat": "Each term has a source word and translations in multiple languages. Select a glossary in the translation page to apply it.",
|
||||
"glossaries.toast.created": "Glossary created",
|
||||
"glossaries.toast.createdDesc": "The glossary \"{name}\" has been created.",
|
||||
"glossaries.toast.imported": "Glossary imported",
|
||||
"glossaries.toast.importedDesc": "The glossary \"{name}\" has been imported.",
|
||||
"glossaries.toast.updated": "Glossary updated",
|
||||
"glossaries.toast.updatedDesc": "The glossary \"{name}\" has been updated.",
|
||||
"glossaries.toast.deleted": "Glossary deleted",
|
||||
"glossaries.toast.deletedDesc": "The glossary has been deleted.",
|
||||
"glossaries.toast.error": "Error",
|
||||
"glossaries.toast.errorCreate": "Failed to create glossary",
|
||||
"glossaries.toast.errorImport": "Failed to import glossary",
|
||||
"glossaries.toast.errorUpdate": "Failed to update glossary",
|
||||
"glossaries.toast.errorDelete": "Failed to delete glossary",
|
||||
"glossaries.dialog.title": "New glossary",
|
||||
"glossaries.dialog.description": "Create a glossary for your translations",
|
||||
"glossaries.dialog.nameLabel": "Name",
|
||||
@@ -1003,6 +1026,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Reconstruction",
|
||||
"dashboard.translate.pipeline.finalize": "Finalisation",
|
||||
"dashboard.translate.progress.failedTitle": "Traduction échouée",
|
||||
"glossaries.yourGlossaries": "Vos glossaires",
|
||||
"glossaries.title": "Glossaires & Contexte",
|
||||
"glossaries.description": "Gérez vos glossaires et instructions de contexte pour des traductions plus précises.",
|
||||
"glossaries.createNew": "Créer nouveau",
|
||||
"glossaries.empty": "Aucun glossaire",
|
||||
"glossaries.emptyDesc": "Créez votre premier glossaire ou chargez un préréglage professionnel ci-dessus",
|
||||
"glossaries.defineTerms": "termes",
|
||||
"glossaries.aboutTitle": "À propos des glossaires",
|
||||
"glossaries.aboutDesc": "Les glossaires vous permettent de définir des traductions exactes pour des termes spécifiques. Lors de la traduction, les termes du glossaire garantissent des traductions cohérentes et précises.",
|
||||
"glossaries.aboutFormat": "Chaque terme a un mot source et des traductions en plusieurs langues. Sélectionnez un glossaire dans la page de traduction pour l'appliquer.",
|
||||
"glossaries.toast.created": "Glossaire créé",
|
||||
"glossaries.toast.createdDesc": "Le glossaire \"{name}\" a été créé.",
|
||||
"glossaries.toast.imported": "Glossaire importé",
|
||||
"glossaries.toast.importedDesc": "Le glossaire \"{name}\" a été importé.",
|
||||
"glossaries.toast.updated": "Glossaire mis à jour",
|
||||
"glossaries.toast.updatedDesc": "Le glossaire \"{name}\" a été mis à jour.",
|
||||
"glossaries.toast.deleted": "Glossaire supprimé",
|
||||
"glossaries.toast.deletedDesc": "Le glossaire a été supprimé.",
|
||||
"glossaries.toast.error": "Erreur",
|
||||
"glossaries.toast.errorCreate": "Impossible de créer le glossaire",
|
||||
"glossaries.toast.errorImport": "Impossible d'importer le glossaire",
|
||||
"glossaries.toast.errorUpdate": "Impossible de mettre à jour le glossaire",
|
||||
"glossaries.toast.errorDelete": "Impossible de supprimer le glossaire",
|
||||
"glossaries.dialog.title": "Nouveau glossaire",
|
||||
"glossaries.dialog.description": "Créez un glossaire pour vos traductions",
|
||||
"glossaries.dialog.nameLabel": "Nom",
|
||||
@@ -1759,6 +1805,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Reconstruir",
|
||||
"dashboard.translate.pipeline.finalize": "Finalizar",
|
||||
"dashboard.translate.progress.failedTitle": "Traducción fallida",
|
||||
"glossaries.yourGlossaries": "Tus glosarios",
|
||||
"glossaries.title": "Glosarios y Contexto",
|
||||
"glossaries.description": "Gestiona tus glosarios e instrucciones de contexto para traducciones más precisas.",
|
||||
"glossaries.createNew": "Crear nuevo",
|
||||
"glossaries.empty": "Sin glosarios",
|
||||
"glossaries.emptyDesc": "Crea tu primer glosario o carga un preset profesional arriba",
|
||||
"glossaries.defineTerms": "términos",
|
||||
"glossaries.aboutTitle": "Sobre los glosarios",
|
||||
"glossaries.aboutDesc": "Los glosarios te permiten definir traducciones exactas para términos específicos. Al traducir, los términos del glosario garantizan traducciones consistentes y precisas.",
|
||||
"glossaries.aboutFormat": "Cada término tiene una palabra fuente y traducciones en varios idiomas. Selecciona un glosario en la página de traducción para aplicarlo.",
|
||||
"glossaries.toast.created": "Glosario creado",
|
||||
"glossaries.toast.createdDesc": "El glosario \"{name}\" ha sido creado.",
|
||||
"glossaries.toast.imported": "Glosario importado",
|
||||
"glossaries.toast.importedDesc": "El glosario \"{name}\" ha sido importado.",
|
||||
"glossaries.toast.updated": "Glosario actualizado",
|
||||
"glossaries.toast.updatedDesc": "El glosario \"{name}\" ha sido actualizado.",
|
||||
"glossaries.toast.deleted": "Glosario eliminado",
|
||||
"glossaries.toast.deletedDesc": "El glosario ha sido eliminado.",
|
||||
"glossaries.toast.error": "Error",
|
||||
"glossaries.toast.errorCreate": "Error al crear el glosario",
|
||||
"glossaries.toast.errorImport": "Error al importar el glosario",
|
||||
"glossaries.toast.errorUpdate": "Error al actualizar el glosario",
|
||||
"glossaries.toast.errorDelete": "Error al eliminar el glosario",
|
||||
"glossaries.dialog.title": "Nuevo glosario",
|
||||
"glossaries.dialog.description": "Crea un glosario para tus traducciones",
|
||||
"glossaries.dialog.nameLabel": "Nombre",
|
||||
@@ -2467,6 +2536,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Rekonstruieren",
|
||||
"dashboard.translate.pipeline.finalize": "Finalisieren",
|
||||
"dashboard.translate.progress.failedTitle": "Übersetzung fehlgeschlagen",
|
||||
"glossaries.yourGlossaries": "Ihre Glossare",
|
||||
"glossaries.title": "Glossare & Kontext",
|
||||
"glossaries.description": "Verwalten Sie Ihre Glossare und Kontextanweisungen für genauere Übersetzungen.",
|
||||
"glossaries.createNew": "Neu erstellen",
|
||||
"glossaries.empty": "Noch keine Glossare",
|
||||
"glossaries.emptyDesc": "Erstellen Sie Ihr erstes Glossar oder laden Sie eine professionelle Vorlage oben",
|
||||
"glossaries.defineTerms": "Begriffe",
|
||||
"glossaries.aboutTitle": "Über Glossare",
|
||||
"glossaries.aboutDesc": "Glossare ermöglichen es Ihnen, exakte Übersetzungen für bestimmte Begriffe zu definieren. Bei der Übersetzung werden die Glossarbegriffe für konsistente und präzise Übersetzungen verwendet.",
|
||||
"glossaries.aboutFormat": "Jeder Begriff hat ein Quellwort und Übersetzungen in mehreren Sprachen. Wählen Sie ein Glossar auf der Übersetzungsseite aus, um es anzuwenden.",
|
||||
"glossaries.toast.created": "Glossar erstellt",
|
||||
"glossaries.toast.createdDesc": "Das Glossar \"{name}\" wurde erstellt.",
|
||||
"glossaries.toast.imported": "Glossar importiert",
|
||||
"glossaries.toast.importedDesc": "Das Glossar \"{name}\" wurde importiert.",
|
||||
"glossaries.toast.updated": "Glossar aktualisiert",
|
||||
"glossaries.toast.updatedDesc": "Das Glossar \"{name}\" wurde aktualisiert.",
|
||||
"glossaries.toast.deleted": "Glossar gelöscht",
|
||||
"glossaries.toast.deletedDesc": "Das Glossar wurde gelöscht.",
|
||||
"glossaries.toast.error": "Fehler",
|
||||
"glossaries.toast.errorCreate": "Glossar konnte nicht erstellt werden",
|
||||
"glossaries.toast.errorImport": "Glossar konnte nicht importiert werden",
|
||||
"glossaries.toast.errorUpdate": "Glossar konnte nicht aktualisiert werden",
|
||||
"glossaries.toast.errorDelete": "Glossar konnte nicht gelöscht werden",
|
||||
"glossaries.dialog.title": "Neues Glossar",
|
||||
"glossaries.dialog.description": "Erstellen Sie ein Glossar für Ihre Übersetzungen",
|
||||
"glossaries.dialog.nameLabel": "Name",
|
||||
@@ -3175,6 +3267,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Reconstruir",
|
||||
"dashboard.translate.pipeline.finalize": "Finalizar",
|
||||
"dashboard.translate.progress.failedTitle": "Tradução falhou",
|
||||
"glossaries.yourGlossaries": "Seus glossários",
|
||||
"glossaries.title": "Glossários e Contexto",
|
||||
"glossaries.description": "Gerencie seus glossários e instruções de contexto para traduções mais precisas.",
|
||||
"glossaries.createNew": "Criar novo",
|
||||
"glossaries.empty": "Nenhum glossário",
|
||||
"glossaries.emptyDesc": "Crie seu primeiro glossário ou carregue um preset profissional acima",
|
||||
"glossaries.defineTerms": "termos",
|
||||
"glossaries.aboutTitle": "Sobre glossários",
|
||||
"glossaries.aboutDesc": "Os glossários permitem definir traduções exatas para termos específicos. Ao traduzir, os termos do glossário garantem traduções consistentes e precisas.",
|
||||
"glossaries.aboutFormat": "Cada termo tem uma palavra fonte e traduções em vários idiomas. Selecione um glossário na página de tradução para aplicá-lo.",
|
||||
"glossaries.toast.created": "Glossário criado",
|
||||
"glossaries.toast.createdDesc": "O glossário \"{name}\" foi criado.",
|
||||
"glossaries.toast.imported": "Glossário importado",
|
||||
"glossaries.toast.importedDesc": "O glossário \"{name}\" foi importado.",
|
||||
"glossaries.toast.updated": "Glossário atualizado",
|
||||
"glossaries.toast.updatedDesc": "O glossário \"{name}\" foi atualizado.",
|
||||
"glossaries.toast.deleted": "Glossário excluído",
|
||||
"glossaries.toast.deletedDesc": "O glossário foi excluído.",
|
||||
"glossaries.toast.error": "Erro",
|
||||
"glossaries.toast.errorCreate": "Falha ao criar glossário",
|
||||
"glossaries.toast.errorImport": "Falha ao importar glossário",
|
||||
"glossaries.toast.errorUpdate": "Falha ao atualizar glossário",
|
||||
"glossaries.toast.errorDelete": "Falha ao excluir glossário",
|
||||
"glossaries.dialog.title": "Novo glossário",
|
||||
"glossaries.dialog.description": "Crie um glossário para suas traduções",
|
||||
"glossaries.dialog.nameLabel": "Nome",
|
||||
@@ -3883,6 +3998,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Ricostruisci",
|
||||
"dashboard.translate.pipeline.finalize": "Finalizza",
|
||||
"dashboard.translate.progress.failedTitle": "Traduzione fallita",
|
||||
"glossaries.yourGlossaries": "I tuoi glossari",
|
||||
"glossaries.title": "Glossari e Contesto",
|
||||
"glossaries.description": "Gestisci i tuoi glossari e le istruzioni di contesto per traduzioni più precise.",
|
||||
"glossaries.createNew": "Crea nuovo",
|
||||
"glossaries.empty": "Nessun glossario",
|
||||
"glossaries.emptyDesc": "Crea il tuo primo glossario o carica un preset professionale sopra",
|
||||
"glossaries.defineTerms": "termini",
|
||||
"glossaries.aboutTitle": "Informazioni sui glossari",
|
||||
"glossaries.aboutDesc": "I glossari ti permettono di definire traduzioni esatte per termini specifici. Durante la traduzione, i termini del glossario garantiscono traduzioni coerenti e precise.",
|
||||
"glossaries.aboutFormat": "Ogni termine ha una parola sorgente e traduzioni in più lingue. Seleziona un glossario nella pagina di traduzione per applicarlo.",
|
||||
"glossaries.toast.created": "Glossario creato",
|
||||
"glossaries.toast.createdDesc": "Il glossario \"{name}\" è stato creato.",
|
||||
"glossaries.toast.imported": "Glossario importato",
|
||||
"glossaries.toast.importedDesc": "Il glossario \"{name}\" è stato importato.",
|
||||
"glossaries.toast.updated": "Glossario aggiornato",
|
||||
"glossaries.toast.updatedDesc": "Il glossario \"{name}\" è stato aggiornato.",
|
||||
"glossaries.toast.deleted": "Glossario eliminato",
|
||||
"glossaries.toast.deletedDesc": "Il glossario è stato eliminato.",
|
||||
"glossaries.toast.error": "Errore",
|
||||
"glossaries.toast.errorCreate": "Impossibile creare il glossario",
|
||||
"glossaries.toast.errorImport": "Impossibile importare il glossario",
|
||||
"glossaries.toast.errorUpdate": "Impossibile aggiornare il glossario",
|
||||
"glossaries.toast.errorDelete": "Impossibile eliminare il glossario",
|
||||
"glossaries.dialog.title": "Nuovo glossario",
|
||||
"glossaries.dialog.description": "Crea un glossario per le tue traduzioni",
|
||||
"glossaries.dialog.nameLabel": "Nome",
|
||||
@@ -4591,6 +4729,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Reconstrueren",
|
||||
"dashboard.translate.pipeline.finalize": "Afronden",
|
||||
"dashboard.translate.progress.failedTitle": "Vertaling mislukt",
|
||||
"glossaries.yourGlossaries": "Uw woordenlijsten",
|
||||
"glossaries.title": "Woordenlijsten & Context",
|
||||
"glossaries.description": "Beheer uw woordenlijsten en contextinstructies voor nauwkeurigere vertalingen.",
|
||||
"glossaries.createNew": "Nieuwe maken",
|
||||
"glossaries.empty": "Nog geen woordenlijsten",
|
||||
"glossaries.emptyDesc": "Maak uw eerste woordenlijst of laad een professionele preset hierboven",
|
||||
"glossaries.defineTerms": "termen",
|
||||
"glossaries.aboutTitle": "Over woordenlijsten",
|
||||
"glossaries.aboutDesc": "Met woordenlijsten kunt u exacte vertalingen definiëren voor specifieke termen. Bij het vertalen worden de termen uit de woordenlijst gebruikt voor consistente en nauwkeurige vertalingen.",
|
||||
"glossaries.aboutFormat": "Elke term heeft een brontaalwoord en vertalingen in meerdere talen. Selecteer een woordenlijst op de vertaalpagina om deze toe te passen.",
|
||||
"glossaries.toast.created": "Woordenlijst gemaakt",
|
||||
"glossaries.toast.createdDesc": "De woordenlijst \"{name}\" is gemaakt.",
|
||||
"glossaries.toast.imported": "Woordenlijst geïmporteerd",
|
||||
"glossaries.toast.importedDesc": "De woordenlijst \"{name}\" is geïmporteerd.",
|
||||
"glossaries.toast.updated": "Woordenlijst bijgewerkt",
|
||||
"glossaries.toast.updatedDesc": "De woordenlijst \"{name}\" is bijgewerkt.",
|
||||
"glossaries.toast.deleted": "Woordenlijst verwijderd",
|
||||
"glossaries.toast.deletedDesc": "De woordenlijst is verwijderd.",
|
||||
"glossaries.toast.error": "Fout",
|
||||
"glossaries.toast.errorCreate": "Kan woordenlijst niet maken",
|
||||
"glossaries.toast.errorImport": "Kan woordenlijst niet importeren",
|
||||
"glossaries.toast.errorUpdate": "Kan woordenlijst niet bijwerken",
|
||||
"glossaries.toast.errorDelete": "Kan woordenlijst niet verwijderen",
|
||||
"glossaries.dialog.title": "Nieuwe woordenlijst",
|
||||
"glossaries.dialog.description": "Maak een woordenlijst voor uw vertalingen",
|
||||
"glossaries.dialog.nameLabel": "Naam",
|
||||
@@ -5299,6 +5460,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "Восстановление",
|
||||
"dashboard.translate.pipeline.finalize": "Завершение",
|
||||
"dashboard.translate.progress.failedTitle": "Перевод не удался",
|
||||
"glossaries.yourGlossaries": "Ваши глоссарии",
|
||||
"glossaries.title": "Глоссарии и контекст",
|
||||
"glossaries.description": "Управляйте глоссариями и контекстными инструкциями для более точных переводов.",
|
||||
"glossaries.createNew": "Создать новый",
|
||||
"glossaries.empty": "Глоссариев пока нет",
|
||||
"glossaries.emptyDesc": "Создайте первый глоссарий или загрузите профессиональный пресет выше",
|
||||
"glossaries.defineTerms": "терминов",
|
||||
"glossaries.aboutTitle": "О глоссариях",
|
||||
"glossaries.aboutDesc": "Глоссарии позволяют определить точные переводы для конкретных терминов. При переводе термины из глоссария обеспечивают последовательные и точные переводы.",
|
||||
"glossaries.aboutFormat": "Каждый термин имеет исходное слово и переводы на несколько языков. Выберите глоссарий на странице перевода, чтобы применить его.",
|
||||
"glossaries.toast.created": "Глоссарий создан",
|
||||
"glossaries.toast.createdDesc": "Глоссарий \"{name}\" создан.",
|
||||
"glossaries.toast.imported": "Глоссарий импортирован",
|
||||
"glossaries.toast.importedDesc": "Глоссарий \"{name}\" импортирован.",
|
||||
"glossaries.toast.updated": "Глоссарий обновлён",
|
||||
"glossaries.toast.updatedDesc": "Глоссарий \"{name}\" обновлён.",
|
||||
"glossaries.toast.deleted": "Глоссарий удалён",
|
||||
"glossaries.toast.deletedDesc": "Глоссарий удалён.",
|
||||
"glossaries.toast.error": "Ошибка",
|
||||
"glossaries.toast.errorCreate": "Не удалось создать глоссарий",
|
||||
"glossaries.toast.errorImport": "Не удалось импортировать глоссарий",
|
||||
"glossaries.toast.errorUpdate": "Не удалось обновить глоссарий",
|
||||
"glossaries.toast.errorDelete": "Не удалось удалить глоссарий",
|
||||
"glossaries.dialog.title": "Новый глоссарий",
|
||||
"glossaries.dialog.description": "Создайте глоссарий для ваших переводов",
|
||||
"glossaries.dialog.nameLabel": "Название",
|
||||
@@ -6009,6 +6193,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "再構築",
|
||||
"dashboard.translate.pipeline.finalize": "完了",
|
||||
"dashboard.translate.progress.failedTitle": "翻訳失敗",
|
||||
"glossaries.yourGlossaries": "あなたの用語集",
|
||||
"glossaries.title": "用語集とコンテキスト",
|
||||
"glossaries.description": "用語集とコンテキスト指示を管理して、より正確な翻訳を実現します。",
|
||||
"glossaries.createNew": "新規作成",
|
||||
"glossaries.empty": "用語集はまだありません",
|
||||
"glossaries.emptyDesc": "最初の用語集を作成するか、上のプロフェッショナルプリセットを読み込んでください",
|
||||
"glossaries.defineTerms": "用語",
|
||||
"glossaries.aboutTitle": "用語集について",
|
||||
"glossaries.aboutDesc": "用語集を使用すると、特定の用語の正確な翻訳を定義できます。翻訳時に用語集の用語が使用され、一貫した正確な翻訳が保証されます。",
|
||||
"glossaries.aboutFormat": "各用語にはソース語と複数言語の翻訳があります。翻訳ページで用語集を選択して適用してください。",
|
||||
"glossaries.toast.created": "用語集を作成しました",
|
||||
"glossaries.toast.createdDesc": "用語集「{name}」を作成しました。",
|
||||
"glossaries.toast.imported": "用語集をインポートしました",
|
||||
"glossaries.toast.importedDesc": "用語集「{name}」をインポートしました。",
|
||||
"glossaries.toast.updated": "用語集を更新しました",
|
||||
"glossaries.toast.updatedDesc": "用語集「{name}」を更新しました。",
|
||||
"glossaries.toast.deleted": "用語集を削除しました",
|
||||
"glossaries.toast.deletedDesc": "用語集を削除しました。",
|
||||
"glossaries.toast.error": "エラー",
|
||||
"glossaries.toast.errorCreate": "用語集の作成に失敗しました",
|
||||
"glossaries.toast.errorImport": "用語集のインポートに失敗しました",
|
||||
"glossaries.toast.errorUpdate": "用語集の更新に失敗しました",
|
||||
"glossaries.toast.errorDelete": "用語集の削除に失敗しました",
|
||||
"glossaries.dialog.title": "新しい用語集",
|
||||
"glossaries.dialog.description": "翻訳用の用語集を作成",
|
||||
"glossaries.dialog.nameLabel": "名前",
|
||||
@@ -6716,6 +6923,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "재구성",
|
||||
"dashboard.translate.pipeline.finalize": "완료",
|
||||
"dashboard.translate.progress.failedTitle": "번역 실패",
|
||||
"glossaries.yourGlossaries": "내 용어집",
|
||||
"glossaries.title": "용어집 및 컨텍스트",
|
||||
"glossaries.description": "용어집과 컨텍스트 지침을 관리하여 더 정확한 번역을하세요.",
|
||||
"glossaries.createNew": "새로 만들기",
|
||||
"glossaries.empty": "용어집이 없습니다",
|
||||
"glossaries.emptyDesc": "첫 번째 용어집을 만들거나 위에서 전문 프리셋을 로드하세요",
|
||||
"glossaries.defineTerms": "용어",
|
||||
"glossaries.aboutTitle": "용어집 정보",
|
||||
"glossaries.aboutDesc": "용어집을 사용하면 특정 용어에 대한 정확한 번역을 정의할 수 있습니다. 번역 시 용어집 용어가 사용되어 일관되고 정확한 번역이 보장됩니다.",
|
||||
"glossaries.aboutFormat": "각 용어에는 원본 단어와 여러 언어의 번역이 있습니다. 번역 페이지에서 용어집을 선택하여 적용하세요.",
|
||||
"glossaries.toast.created": "용어집 생성됨",
|
||||
"glossaries.toast.createdDesc": "용어집 \"{name}\"이(가) 생성되었습니다.",
|
||||
"glossaries.toast.imported": "용어집 가져오기 완료",
|
||||
"glossaries.toast.importedDesc": "용어집 \"{name}\"을(를) 가져왔습니다.",
|
||||
"glossaries.toast.updated": "용어집 업데이트됨",
|
||||
"glossaries.toast.updatedDesc": "용어집 \"{name}\"이(가) 업데이트되었습니다.",
|
||||
"glossaries.toast.deleted": "용어집 삭제됨",
|
||||
"glossaries.toast.deletedDesc": "용어집이 삭제되었습니다.",
|
||||
"glossaries.toast.error": "오류",
|
||||
"glossaries.toast.errorCreate": "용어집 생성 실패",
|
||||
"glossaries.toast.errorImport": "용어집 가져오기 실패",
|
||||
"glossaries.toast.errorUpdate": "용어집 업데이트 실패",
|
||||
"glossaries.toast.errorDelete": "용어집 삭제 실패",
|
||||
"glossaries.dialog.title": "새 용어집",
|
||||
"glossaries.dialog.description": "번역을 위한 용어집을 만드세요",
|
||||
"glossaries.dialog.nameLabel": "이름",
|
||||
@@ -7423,6 +7653,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "重建",
|
||||
"dashboard.translate.pipeline.finalize": "完成",
|
||||
"dashboard.translate.progress.failedTitle": "翻译失败",
|
||||
"glossaries.yourGlossaries": "您的术语表",
|
||||
"glossaries.title": "术语表与上下文",
|
||||
"glossaries.description": "管理您的术语表和上下文指令,以获得更准确的翻译。",
|
||||
"glossaries.createNew": "新建",
|
||||
"glossaries.empty": "暂无术语表",
|
||||
"glossaries.emptyDesc": "创建您的第一个术语表或加载上方的专业预设",
|
||||
"glossaries.defineTerms": "术语",
|
||||
"glossaries.aboutTitle": "关于术语表",
|
||||
"glossaries.aboutDesc": "术语表允许您为特定术语定义准确的翻译。翻译时,术语表中的术语可确保翻译的一致性和准确性。",
|
||||
"glossaries.aboutFormat": "每个术语都有一个源词和多种语言的翻译。在翻译页面选择术语表即可应用。",
|
||||
"glossaries.toast.created": "术语表已创建",
|
||||
"glossaries.toast.createdDesc": "术语表\"{name}\"已创建。",
|
||||
"glossaries.toast.imported": "术语表已导入",
|
||||
"glossaries.toast.importedDesc": "术语表\"{name}\"已导入。",
|
||||
"glossaries.toast.updated": "术语表已更新",
|
||||
"glossaries.toast.updatedDesc": "术语表\"{name}\"已更新。",
|
||||
"glossaries.toast.deleted": "术语表已删除",
|
||||
"glossaries.toast.deletedDesc": "术语表已删除。",
|
||||
"glossaries.toast.error": "错误",
|
||||
"glossaries.toast.errorCreate": "创建术语表失败",
|
||||
"glossaries.toast.errorImport": "导入术语表失败",
|
||||
"glossaries.toast.errorUpdate": "更新术语表失败",
|
||||
"glossaries.toast.errorDelete": "删除术语表失败",
|
||||
"glossaries.dialog.title": "新建术语表",
|
||||
"glossaries.dialog.description": "为您的翻译创建术语表",
|
||||
"glossaries.dialog.nameLabel": "名称",
|
||||
@@ -8088,6 +8341,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "إعادة بناء",
|
||||
"dashboard.translate.pipeline.finalize": "إنهاء",
|
||||
"dashboard.translate.progress.failedTitle": "فشلت الترجمة",
|
||||
"glossaries.yourGlossaries": "معاجمك",
|
||||
"glossaries.title": "المعاجم والسياق",
|
||||
"glossaries.description": "قم بإدارة معاجمك وتعليمات السياق للحصول على ترجمات أكثر دقة.",
|
||||
"glossaries.createNew": "إنشاء جديد",
|
||||
"glossaries.empty": "لا توجد معاجم بعد",
|
||||
"glossaries.emptyDesc": "أنشئ أول معجم لك أو حمّل إعدادًا مسبقًا احترافيًا أعلاه",
|
||||
"glossaries.defineTerms": "مصطلحات",
|
||||
"glossaries.aboutTitle": "حول المعاجم",
|
||||
"glossaries.aboutDesc": "تتيح لك المعاجم تحديد ترجمات دقيقة لمصطلحات معينة. عند الترجمة، يتم استخدام مصطلحات المعجم لضمان ترجمات متسقة ودقيقة.",
|
||||
"glossaries.aboutFormat": "كل مصطلح له كلمة مصدر وترجمات بلغات متعددة. حدد معجمًا في صفحة الترجمة لتطبيقه.",
|
||||
"glossaries.toast.created": "تم إنشاء المعجم",
|
||||
"glossaries.toast.createdDesc": "تم إنشاء المعجم \"{name}\".",
|
||||
"glossaries.toast.imported": "تم استيراد المعجم",
|
||||
"glossaries.toast.importedDesc": "تم استيراد المعجم \"{name}\".",
|
||||
"glossaries.toast.updated": "تم تحديث المعجم",
|
||||
"glossaries.toast.updatedDesc": "تم تحديث المعجم \"{name}\".",
|
||||
"glossaries.toast.deleted": "تم حذف المعجم",
|
||||
"glossaries.toast.deletedDesc": "تم حذف المعجم.",
|
||||
"glossaries.toast.error": "خطأ",
|
||||
"glossaries.toast.errorCreate": "فشل إنشاء المعجم",
|
||||
"glossaries.toast.errorImport": "فشل استيراد المعجم",
|
||||
"glossaries.toast.errorUpdate": "فشل تحديث المعجم",
|
||||
"glossaries.toast.errorDelete": "فشل حذف المعجم",
|
||||
"glossaries.dialog.title": "معجم جديد",
|
||||
"glossaries.dialog.description": "أنشئ معجمًا لترجماتك",
|
||||
"glossaries.dialog.nameLabel": "الاسم",
|
||||
@@ -8762,6 +9038,29 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
"dashboard.translate.pipeline.rebuild": "بازسازی",
|
||||
"dashboard.translate.pipeline.finalize": "نهاییسازی",
|
||||
"dashboard.translate.progress.failedTitle": "ترجمه ناموفق",
|
||||
"glossaries.yourGlossaries": "واژهنامههای شما",
|
||||
"glossaries.title": "واژهنامهها و زمینه",
|
||||
"glossaries.description": "واژهنامهها و دستورالعملهای زمینه خود را برای ترجمه دقیقتر مدیریت کنید.",
|
||||
"glossaries.createNew": "ایجاد جدید",
|
||||
"glossaries.empty": "هنوز واژهنامهای نیست",
|
||||
"glossaries.emptyDesc": "اولین واژهنامه خود را ایجاد کنید یا یک پیشتنظیم حرفهای بالا را بارگذاری کنید",
|
||||
"glossaries.defineTerms": "واژه",
|
||||
"glossaries.aboutTitle": "درباره واژهنامهها",
|
||||
"glossaries.aboutDesc": "واژهنامهها به شما امکان تعریف ترجمه دقیق برای اصطلاحات خاص را میدهند. هنگام ترجمه، اصطلاحات واژهنامه برای اطمینان از ترجمه سازگار و دقیق استفاده میشوند.",
|
||||
"glossaries.aboutFormat": "هر اصطلاح یک کلمه منبع و ترجمههایی به چندین زبان دارد. در صفحه ترجمه یک واژهنامه را انتخاب کنید تا اعمال شود.",
|
||||
"glossaries.toast.created": "واژهنامه ایجاد شد",
|
||||
"glossaries.toast.createdDesc": "واژهنامه \"{name}\" ایجاد شد.",
|
||||
"glossaries.toast.imported": "واژهنامه وارد شد",
|
||||
"glossaries.toast.importedDesc": "واژهنامه \"{name}\" وارد شد.",
|
||||
"glossaries.toast.updated": "واژهنامه بهروزرسانی شد",
|
||||
"glossaries.toast.updatedDesc": "واژهنامه \"{name}\" بهروزرسانی شد.",
|
||||
"glossaries.toast.deleted": "واژهنامه حذف شد",
|
||||
"glossaries.toast.deletedDesc": "واژهنامه حذف شد.",
|
||||
"glossaries.toast.error": "خطا",
|
||||
"glossaries.toast.errorCreate": "ایجاد واژهنامه ناموفق بود",
|
||||
"glossaries.toast.errorImport": "وارد کردن واژهنامه ناموفق بود",
|
||||
"glossaries.toast.errorUpdate": "بهروزرسانی واژهنامه ناموفق بود",
|
||||
"glossaries.toast.errorDelete": "حذف واژهنامه ناموفق بود",
|
||||
"glossaries.dialog.title": "واژهنامه جدید",
|
||||
"glossaries.dialog.description": "یک واژهنامه برای ترجمههای خود بسازید",
|
||||
"glossaries.dialog.nameLabel": "نام",
|
||||
|
||||
Reference in New Issue
Block a user