fix(glossaries): correct source/target display mapping (FR=source, EN=target, others=translations[lang])
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m55s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m55s
Avant : getDisplaySource(term, 'en') lisait term.translations.en (qui n'existe pas) puis fallback sur term.source = francais. C'est ce qui affichait du francais et du néerlandais au mauvais endroit. Apres : le mapping reflete la structure reelle des donnees : - FR (lang='fr') → term.source - EN (lang='en') → term.target - autres (de, es, it, pt, nl, ru, ja, ko, zh, ar, fa) → term.translations[lang] - si manquant → '' (placeholder, JAMAIS une autre langue en fallback) Memes regles pour getDisplayTarget, inversees (defaut = target). Edition (handleTermChange) ecrit au bon endroit : - FR → term.source - EN (ou multi) → term.target - autres → translations[lang] Le remap automatique de term.target au changement de targetLanguage est supprime (lecture a la volee maintenant, plus besoin de modifier l'etat des termes). Aucun changement de donnees, aucun changement backend, aucun changement de schema. Fix purement frontend.
This commit is contained in:
@@ -26,28 +26,38 @@ import { ProUpgradePrompt } from '../ProUpgradePrompt';
|
|||||||
|
|
||||||
const MAX_TERMS = 500;
|
const MAX_TERMS = 500;
|
||||||
|
|
||||||
function getDisplayTarget(
|
/** Source term in the chosen language.
|
||||||
term: { target: string; translations?: Record<string, string> | null },
|
* Structure du terme : source=FR (fondation), target=EN (fondation),
|
||||||
lang: string
|
* translations = 11 autres langues (de, es, it, pt, nl, ru, ja, ko, zh, ar, fa).
|
||||||
): string {
|
* - lang vide / 'multi' → '' (pas de choix)
|
||||||
if (lang === 'multi' || lang === 'en' || !lang) return term.target;
|
* - lang === 'fr' → term.source
|
||||||
const translations = term.translations || {};
|
* - lang === 'en' → term.target (fallback EN depuis le champ legacy)
|
||||||
return translations[lang] || term.target;
|
* - autre lang → term.translations[lang]
|
||||||
}
|
* - si manquant → '' (placeholder, JAMAIS une autre langue en fallback)
|
||||||
|
|
||||||
/** Source term in the given language.
|
|
||||||
* - 'fr' → term.source (the original French)
|
|
||||||
* - 'multi' / '' / undefined → term.source (no language chosen, default to FR)
|
|
||||||
* - other langs → term.translations[lang] (the translated source)
|
|
||||||
* - falls back to term.source if missing
|
|
||||||
*/
|
*/
|
||||||
function getDisplaySource(
|
function getDisplaySource(
|
||||||
term: { source: string; translations?: Record<string, string> | null },
|
term: { source: string; target: string; translations?: Record<string, string> | null },
|
||||||
lang: string
|
lang: string
|
||||||
): string {
|
): string {
|
||||||
if (!lang || lang === 'multi' || lang === 'fr') return term.source;
|
if (!lang || lang === 'multi') return '';
|
||||||
|
if (lang === 'fr') return term.source;
|
||||||
|
if (lang === 'en') return term.target;
|
||||||
const translations = term.translations || {};
|
const translations = term.translations || {};
|
||||||
return translations[lang] || term.source;
|
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.
|
||||||
|
*/
|
||||||
|
function getDisplayTarget(
|
||||||
|
term: { source: string; target: string; translations?: Record<string, string> | null },
|
||||||
|
lang: 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;
|
||||||
|
const translations = term.translations || {};
|
||||||
|
return translations[lang] || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GlossaryDetailPage() {
|
export default function GlossaryDetailPage() {
|
||||||
@@ -139,51 +149,35 @@ export default function GlossaryDetailPage() {
|
|||||||
if (i !== index) return t;
|
if (i !== index) return t;
|
||||||
const translations = { ...(t.translations || {}) } as Record<string, string>;
|
const translations = { ...(t.translations || {}) } as Record<string, string>;
|
||||||
|
|
||||||
if (field === 'source') {
|
// FR et EN sont dans les champs legacy (term.source / term.target),
|
||||||
if (!sourceLanguage || sourceLanguage === 'fr') {
|
// les 11 autres langues dans translations[lang].
|
||||||
// French source → write to term.source
|
// Pour l'édition, on écrit au bon endroit selon la langue choisie.
|
||||||
return { ...t, source: value };
|
const isFr = field === 'source' ? sourceLanguage === 'fr' : targetLanguage === 'fr';
|
||||||
}
|
const isEn = field === 'source' ? sourceLanguage === 'en' : targetLanguage === 'en';
|
||||||
// Other source languages → write to translations[sourceLanguage]
|
const otherLang = field === 'source' ? sourceLanguage : targetLanguage;
|
||||||
translations[sourceLanguage] = value;
|
|
||||||
return { ...t, source: t.source, translations };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field === 'target') {
|
if (isFr) {
|
||||||
if (!targetLanguage || targetLanguage === 'multi' || targetLanguage === 'en') {
|
// Écriture du français → champ source (legacy)
|
||||||
// Default target → write to term.target
|
return { ...t, source: value };
|
||||||
return { ...t, target: value };
|
|
||||||
}
|
|
||||||
// Other target languages → write to translations[targetLanguage]
|
|
||||||
translations[targetLanguage] = value;
|
|
||||||
return { ...t, target: t.target, translations };
|
|
||||||
}
|
}
|
||||||
|
if (isEn || !otherLang || otherLang === 'multi') {
|
||||||
return t;
|
// É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 handleSourceLanguageChange = (newLang: string) => {
|
const handleSourceLanguageChange = (newLang: string) => {
|
||||||
setSourceLanguage(newLang);
|
setSourceLanguage(newLang);
|
||||||
// No term remapping needed — the display reads translations[newLang] on the fly.
|
// No remap : l'affichage lit translations[newLang] à la volée.
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTargetLanguageChange = (newLang: string) => {
|
const handleTargetLanguageChange = (newLang: string) => {
|
||||||
if (glossary) {
|
setTargetLanguage(newLang);
|
||||||
setTargetLanguage(newLang);
|
// No remap : l'affichage lit translations[newLang] à la volée.
|
||||||
if (newLang === 'multi' || newLang === 'en') return;
|
|
||||||
// Remap each term to show translation for the new language (when available)
|
|
||||||
setTerms((prev) =>
|
|
||||||
prev.map((t) => {
|
|
||||||
const translations = (t.translations || {}) as Record<string, string>;
|
|
||||||
const langTarget = translations[newLang];
|
|
||||||
if (langTarget) {
|
|
||||||
return { ...t, target: langTarget };
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user