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.
Revert du commit e11a6b1 : la langue source doit etre selectionnable
(car l'utilisateur peut vouloir traduire depuis n'importe quelle
des 12 langues supportees, pas seulement le francais).
Le data modele support deja le cas : chaque terme a un champ
\ ranslations\ (dict de 11 langues) qui contient la traduction du
terme source. Donc pour traduire depuis l'italien, on lit
\ erm.translations.it\ comme source, et \ erm.translations.es\
comme cible si la cible est l'espagnol.
Changements :
- Le combobox 'Langue source' est restaure (12 langues)
- Nouvelle fonction \getDisplaySource(term, lang)\ :
* 'fr' ou 'multi' → term.source (le francais original)
* autre → term.translations[lang] (la traduction dans la langue)
* fallback → term.source si la traduction manque
- handleTermChange ecrit au bon endroit selon la langue :
* source FR → term.source
* autre source → term.translations[sourceLanguage]
* target 'multi'/'en' → term.target
* autre target → term.translations[targetLanguage]
- hasUnsavedChanges compare aussi le dict translations (avant
il ne comparait que source|target, donc un edit dans une autre
langue ne declenchait pas l'alerte 'non enregistre')
- Note sous le combobox source explique la regle
(FR = source originale, autre = champ translations)
- i18n : nouvelle cle \glossaries.detail.sourceLangNote\
ajoutee aux 13 locales (FR + EN traduit)
L'utilisateur peut maintenant choisir 'Italien' comme source et
'Espagnol' comme cible, et voir les termes correspondants.
Les templates data/glossaires/*.json ne stockent les termes sources
qu'en francais. Le combobox 'Langue source' laissait l'utilisateur
croire qu'il pouvait traduire depuis une autre langue, mais le
backend renverrait toujours des termes en francais.
Fix : remplacer le select par un label fixe 'Francais' avec un badge
'fixe' et une note explicative indiquant que le multilingue source
est sur la roadmap.
Le select 'Langue cible' reste : il determine quelle traduction du
terme est affichee dans la colonne 'Cible' (FR+10 langues via le
champ translations).
Le index.json avait ete mis a jour '→ Multilingue' mais les fichiers
data/glossaries/*.json gardaient l'ancien nom '→ Anglais' (commit c66252b
n'avait touche que l'index, pas les donnees). Consequence : importer un
template creait un glossaire avec le nom '→ Anglais' alors que les termes
sont en 11 langues (multilingues).
Sync de name + description des 8 fichiers sur l'index.
UX refonte :
- Retire la section 'Glossaires professionnels' de la vue principale
(les 8 cartes de templates sont maintenant dans le dialog de creation)
- Cartes 'Vos glossaires' plus simples : nom, langues, termes, date
- Cliquer sur la carte navigue vers /dashboard/glossaries/[id]
- Plus de boutons Edit/Delete sur la carte (deplaces dans la page detail)
- Recherche par nom (visible si > 3 glossaires)
- Badge 'Non enregistre' si modifications non sauvegardees
Nouvelle page /dashboard/glossaries/[id] :
- Edition inline du nom (input), langues source/cible (select)
- Tableau des termes avec recherche et edition en place
- Ajout/suppression de termes (max 500)
- Export / Import CSV (meme logique que l'edit dialog)
- Zone danger : confirmation en 2 temps pour la suppression
- Back link vers la liste
- i18n : 40 nouvelles cles ajoutees aux 13 locales (FR + EN traduit,
les autres utilisent le fallback EN)
Design preserve : editorial-card, brand-accent, meme typographie,
meme palette. Refactor structurel uniquement, pas de restyling.
Le system prompt (Instructions de contexte) reste tel quel, au-dessus
de la liste des glossaires, comme dans le design actuel.
Nouveau script dedie a la migration 'multilingue uniquement' :
- Supprime tout glossaire dont le nom ne contient pas '-> Multilingue'
- Backup JSON integral des glossaires + termes avant suppression
- Mode --dry-run / confirmation interactive / --yes
- Heuristique par nom uniquement (target_language peut etre 'multi' partout
apres les migrations passees, donc non fiable)
Utilise apres le dedup (user_id, name) pour finir la migration.
Le groupement par template_id etait faux sur la prod :
- Les doublons historiques ont template_id=NULL (crees avant la migration)
- Deux glossaires 'Finance - FR->Anglais' et 'Finance - FR->Multilingue'
partagent le meme template_id mais DOIVENT etre conserves separement.
Changements :
- Groupement par (user_id, name) -> c'est ce que l'utilisateur voit dans l'UI
et la definition reelle d'un doublon.
- Les glossaires multilingues ('-> Multilingue') ont un nom distinct des
versions '-> Anglais' : ils ne sont jamais fusionnes (preserve par design).
- Fallback automatique si la colonne template_id est absente du schema
(dev DB) : warning + requete sans la colonne, aucun crash.
- Suppression du flag --allow-missing-template-id devenu inutile.
- Nettoyage des imports ORM inutiles (text brut uniquement, plus rapide).
- scripts/backup_duplicate_glossaries.py : exporte en JSON les doublons
(meme user_id + template_id) sans rien supprimer. Schema validation,
tri stable, mode degrade si colonne template_id absente.
- scripts/delete_duplicate_glossaries.py : lit un backup JSON et supprime
les doublons listes. Validation IDs, confirmation interactive,
commit par user, mode --dry-run / --yes.
- .gitea/workflows/cleanup-glossaries.yml : workflow_dispatch qui SSH
sur le serveur de prod et execute le script dans le conteneur backend
(postgres demarre, .env charge, env_file docker-compose).
- Add template_id column to Glossary model (nullable, indexed)
- Backend: return 409 Conflict if user already imported a template
- Frontend: disable preset cards already imported, show 'Imported' badge
- Fix duplicated text in GlossarySelector source warning (hardcoded FR text removed)
- Complete i18n migration for glossaries page and GlossarySelector
- Add glossaries.presets.alreadyImported key in all 13 locales
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>
When the user changes the target language dropdown in the edit dialog,
terms now remap to show the language-specific translation from the
translations dict. E.g. changing to Spanish shows 'servidor' instead
of 'server'. Falls back to default English if no translation exists.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Users can now change the language pair when editing a glossary:
- EditGlossaryDialog has source/target language dropdowns
- Default target_language changed from 'en' to 'multi' in create dialog
- onSave passes source_language and target_language to the backend
- Backend PATCH endpoint already supports updating these fields
Also fixes:
- CreateGlossaryDialog defaults to 'multi' instead of 'en'
- SUPPORTED_LANGUAGES now includes 'multi' option for target
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
18 glossaries accumulated from multiple imports. This migration:
1. Deletes ALL glossaries with target_language='en' (old stale imports)
2. Deduplicates multilingual glossaries keeping only the newest per name
Result: 8 clean glossaries (one per template), all marked as multilingual.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The /dashboard/glossaries page had two issues:
1. SUPPORTED_LANGUAGES had no 'multi' code, so multilingual glossaries
showed '🌐 undefined' instead of '🌐 Multilingue'
2. The Compatible/Autre cible badge compared target_language directly
with currentTargetLang, so 'multi' never matched → always showed
'Autre cible'. Now 'multi' is treated as compatible with any target.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Old glossaries imported before enrichment have target_language='en' and no
translations dict — they're stale duplicates. This migration:
1. Deletes old FR→EN glossaries with empty/null translations (no Persian, etc.)
2. Renames multilingual glossaries from 'Anglais' to 'Multilingue'
3. Sets ALL remaining FR glossaries to target_language='multi'
4. Users re-import from enriched templates to get Persian/Arabic/etc. terms
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two issues found in screenshot review:
1. Glossary preview always showed English 'target' field (foie → liver)
instead of the language-specific translation. Now looks up
term.translations[targetLang] first, falls back to term.target.
When target is Persian, preview shows 'foie → کبد' (Persian).
2. Previous migration b7c8d9e0f1a2 was already applied on server before
the rename was added. New migration c8d9e0f1a2b3 handles the rename
separately: glossaries with target_language='multi' get their name
changed from 'Anglais' to 'Multilingue'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full audit found 18 issues across backend, frontend, and data files.
Root cause: target_language had no single source of truth — 'en' was
hardcoded as default in 6+ places while templates are actually multilingual.
Fixes applied:
- routes/glossary_routes.py: list_glossaries() fallback 'en' → 'multi'
- routes/glossary_routes.py: import reads target_lang from index.json
(source of truth) instead of template file
- data/glossaries/*.json: all 8 template files target_lang 'en' → 'multi'
- services/glossary_service.py: get_glossary_terms() now returns
target_language field (was missing entirely)
- schemas/glossary_schemas.py: all defaults 'en' → 'multi'
(GlossaryCreate, GlossaryResponse, GlossaryListItem)
- useTranslationConfig.ts: only reset glossary on sourceLang change,
not targetLang (multilingual glossaries work with any target)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The migration already sets target_language='multi' but the glossary name
in the DB still said 'Français → Anglais'. Now it renames them to
'Français → Multilingue' so users aren't confused when translating to
Persian, Arabic, etc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous migration used revision 'a1b2c3d4e5f6' which was already
taken by 005_add_reset_token_to_users.py, causing a cycle. Also both
f6a7b8c9d0e1 and the new migration pointed to the same down_revision.
Fixed:
- New unique revision ID: b7c8d9e0f1a2
- down_revision points to f6a7b8c9d0e1 (current head)
- Chain: e5b2c9d1f4a8 → f6a7b8c9d0e1 → b7c8d9e0f1a2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Templates enriched by enrich_glossary_templates.py already contain
translations for de, es, it, pt, nl, ru, ja, ko, zh, ar, fa (including
Persian). But they were labeled FR→EN, causing incorrect filtering and
warnings when translating to other languages.
Changes:
- index.json: set target_lang='multi' for all 8 templates
- GlossarySelector: treat target_language='multi' as compatible with
any target language (no false warnings, auto-select works)
- GlossarySelector: display '🌐 MULTILINGUE' badge instead of EN flag
- glossary_routes: default target_language to 'multi' instead of 'en'
- Migration: detect existing multilingual glossaries in DB (5+ keys in
translations JSON) and set their target_language to 'multi'
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When activating the glossary toggle, only auto-select a glossary if it
matches BOTH source and target language. If no matching glossary exists
(e.g. translating to Persian but only have FR→EN glossaries), leave the
selector empty so the user picks manually instead of forcing an
incompatible glossary.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>