fix(glossary): resolve data loss for non-FR/EN languages, fix prompt injection reference notes, and classic mode label wording
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m25s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m25s
This commit is contained in:
@@ -37,25 +37,24 @@ const MAX_TERMS = 500;
|
||||
*/
|
||||
function getDisplaySource(
|
||||
term: { source: string; target: string; translations?: Record<string, string> | null },
|
||||
lang: string
|
||||
lang: string,
|
||||
glossarySourceLang: string
|
||||
): string {
|
||||
if (!lang || lang === 'multi') return '';
|
||||
if (lang === 'fr') return term.source;
|
||||
if (lang === 'en') return term.target;
|
||||
if (lang === glossarySourceLang) return term.source;
|
||||
const translations = term.translations || {};
|
||||
return translations[lang] || '';
|
||||
}
|
||||
|
||||
/** Target term in the chosen language. Mêmes règles que getDisplaySource,
|
||||
* mais l'inverse : EN est le défaut (champ target), FR vient de source.
|
||||
/** Target term in the chosen language.
|
||||
*/
|
||||
function getDisplayTarget(
|
||||
term: { source: string; target: string; translations?: Record<string, string> | null },
|
||||
lang: string
|
||||
lang: string,
|
||||
glossaryTargetLang: string
|
||||
): string {
|
||||
if (!lang || lang === 'multi') return term.target; // multi = défaut = EN
|
||||
if (lang === 'en') return term.target;
|
||||
if (lang === 'fr') return term.source;
|
||||
if (!lang) return '';
|
||||
if (lang === 'multi' || lang === glossaryTargetLang) return term.target;
|
||||
const translations = term.translations || {};
|
||||
return translations[lang] || '';
|
||||
}
|
||||
@@ -145,39 +144,82 @@ export default function GlossaryDetailPage() {
|
||||
};
|
||||
|
||||
const handleTermChange = (index: number, field: 'source' | 'target', value: string) => {
|
||||
if (!glossary) return;
|
||||
setTerms(terms.map((t, i) => {
|
||||
if (i !== index) return t;
|
||||
const translations = { ...(t.translations || {}) } as Record<string, string>;
|
||||
|
||||
// FR et EN sont dans les champs legacy (term.source / term.target),
|
||||
// les 11 autres langues dans translations[lang].
|
||||
// Pour l'édition, on écrit au bon endroit selon la langue choisie.
|
||||
const isFr = field === 'source' ? sourceLanguage === 'fr' : targetLanguage === 'fr';
|
||||
const isEn = field === 'source' ? sourceLanguage === 'en' : targetLanguage === 'en';
|
||||
const otherLang = field === 'source' ? sourceLanguage : targetLanguage;
|
||||
const editLang = field === 'source' ? sourceLanguage : targetLanguage;
|
||||
|
||||
if (isFr) {
|
||||
// Écriture du français → champ source (legacy)
|
||||
return { ...t, source: value };
|
||||
if (field === 'source') {
|
||||
if (editLang === glossary.source_language) {
|
||||
return { ...t, source: value };
|
||||
} else {
|
||||
if (editLang && editLang !== 'multi') {
|
||||
translations[editLang] = value;
|
||||
}
|
||||
return { ...t, translations };
|
||||
}
|
||||
} else {
|
||||
if (editLang === 'multi' || editLang === glossary.target_language) {
|
||||
return { ...t, target: value };
|
||||
} else {
|
||||
if (editLang && editLang !== 'multi') {
|
||||
translations[editLang] = value;
|
||||
}
|
||||
return { ...t, translations };
|
||||
}
|
||||
}
|
||||
if (isEn || !otherLang || otherLang === 'multi') {
|
||||
// Écriture de l'anglais (ou défaut multi) → champ target (legacy)
|
||||
return { ...t, target: value };
|
||||
}
|
||||
// Autre langue → translations[otherLang]
|
||||
translations[otherLang] = value;
|
||||
return { ...t, source: t.source, target: t.target, translations };
|
||||
}));
|
||||
};
|
||||
|
||||
const migrateTerms = (
|
||||
currentTerms: GlossaryTermInput[],
|
||||
oldSrc: string,
|
||||
newSrc: string,
|
||||
oldTgt: string,
|
||||
newTgt: string
|
||||
): GlossaryTermInput[] => {
|
||||
if (!glossary) return currentTerms;
|
||||
return currentTerms.map(t => {
|
||||
const languagesData: Record<string, string> = { ...(t.translations || {}) };
|
||||
|
||||
const oldSrcVal = oldSrc === glossary.source_language ? t.source : (t.translations?.[oldSrc] || '');
|
||||
const oldTgtVal = oldTgt === 'multi' || oldTgt === glossary.target_language ? t.target : (t.translations?.[oldTgt] || '');
|
||||
|
||||
if (oldSrc && oldSrc !== 'multi') {
|
||||
languagesData[oldSrc] = oldSrcVal;
|
||||
}
|
||||
if (oldTgt) {
|
||||
const langKey = oldTgt === 'multi' ? 'en' : oldTgt;
|
||||
languagesData[langKey] = oldTgtVal;
|
||||
}
|
||||
|
||||
const newSourceVal = languagesData[newSrc] || '';
|
||||
const targetLangKey = newTgt === 'multi' ? 'en' : newTgt;
|
||||
const newTargetVal = languagesData[targetLangKey] || '';
|
||||
|
||||
delete languagesData[newSrc];
|
||||
delete languagesData[targetLangKey];
|
||||
|
||||
return {
|
||||
source: newSourceVal,
|
||||
target: newTargetVal,
|
||||
translations: languagesData
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const handleSourceLanguageChange = (newLang: string) => {
|
||||
const updated = migrateTerms(terms, sourceLanguage, newLang, targetLanguage, targetLanguage);
|
||||
setSourceLanguage(newLang);
|
||||
// No remap : l'affichage lit translations[newLang] à la volée.
|
||||
setTerms(updated);
|
||||
};
|
||||
|
||||
const handleTargetLanguageChange = (newLang: string) => {
|
||||
const updated = migrateTerms(terms, sourceLanguage, sourceLanguage, targetLanguage, newLang);
|
||||
setTargetLanguage(newLang);
|
||||
// No remap : l'affichage lit translations[newLang] à la volée.
|
||||
setTerms(updated);
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
@@ -520,7 +562,7 @@ export default function GlossaryDetailPage() {
|
||||
>
|
||||
<td className="px-6 py-1.5">
|
||||
<input
|
||||
value={getDisplaySource(term, sourceLanguage)}
|
||||
value={getDisplaySource(term, sourceLanguage, glossary.source_language)}
|
||||
onChange={(e) => handleTermChange(term._index, 'source', e.target.value)}
|
||||
disabled={isUpdating}
|
||||
placeholder={t('glossaries.detail.sourcePlaceholder') || 'terme source'}
|
||||
@@ -529,7 +571,7 @@ export default function GlossaryDetailPage() {
|
||||
</td>
|
||||
<td className="px-3 py-1.5">
|
||||
<input
|
||||
value={getDisplayTarget(term, targetLanguage)}
|
||||
value={getDisplayTarget(term, targetLanguage, glossary.target_language)}
|
||||
onChange={(e) => handleTermChange(term._index, 'target', e.target.value)}
|
||||
disabled={isUpdating}
|
||||
placeholder={t('glossaries.detail.targetPlaceholder') || 'terme cible'}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"translate.glossary.noGlossaryForPair": "No glossary for",
|
||||
"translate.glossary.noGlossaries": "No glossaries yet",
|
||||
"translate.glossary.loading": "Loading...",
|
||||
"translate.glossary.classicMode": "Neutral engine without glossary (AI only)",
|
||||
"translate.glossary.classicMode": "Classic engine (no glossary - Pro LLM mode required)",
|
||||
"translate.glossary.selectPlaceholder": "Select a glossary...",
|
||||
"translate.glossary.multilingual": "MULTILINGUAL",
|
||||
"translate.glossary.noGlossaryAvailable": "No glossary available",
|
||||
@@ -38,7 +38,7 @@
|
||||
"translate.glossary.sourceTerm": "Source term",
|
||||
"translate.glossary.translation": "Translation",
|
||||
"translate.glossary.addTerm": "Add term",
|
||||
"translate.glossary.disabledMode": "Neutral engine without glossary applied",
|
||||
"translate.glossary.disabledMode": "Classic engine without glossary applied",
|
||||
"translate.glossary.addTermError": "Error adding term",
|
||||
"translate.glossary.networkError": "Network error",
|
||||
"translate.glossary.importFailed": "Import failed ({status})",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"translate.glossary.noGlossaryForPair": "Aucun glossaire pour",
|
||||
"translate.glossary.noGlossaries": "Aucun glossaire",
|
||||
"translate.glossary.loading": "Chargement...",
|
||||
"translate.glossary.classicMode": "Moteur neutre sans glossaire (IA uniquement)",
|
||||
"translate.glossary.classicMode": "Moteur classique (sans glossaire - Mode Pro LLM requis)",
|
||||
"translate.glossary.selectPlaceholder": "Sélectionner un glossaire...",
|
||||
"translate.glossary.multilingual": "MULTILINGUE",
|
||||
"translate.glossary.noGlossaryAvailable": "Aucun glossaire disponible",
|
||||
@@ -38,7 +38,7 @@
|
||||
"translate.glossary.sourceTerm": "Terme Source",
|
||||
"translate.glossary.translation": "Traduction",
|
||||
"translate.glossary.addTerm": "Ajouter le terme",
|
||||
"translate.glossary.disabledMode": "Moteur neutre sans glossaire appliqué",
|
||||
"translate.glossary.disabledMode": "Moteur classique sans glossaire appliqué",
|
||||
"translate.glossary.addTermError": "Erreur lors de l'ajout du terme",
|
||||
"translate.glossary.networkError": "Erreur réseau",
|
||||
"translate.glossary.importFailed": "Échec de l'importation ({status})",
|
||||
|
||||
Reference in New Issue
Block a user