diff --git a/app/analyse/interface.py b/app/analyse/interface.py
index b8a68c2..3eb0078 100644
--- a/app/analyse/interface.py
+++ b/app/analyse/interface.py
@@ -1,4 +1,5 @@
import streamlit as st
+from utils.translations import _
from .sankey import afficher_sankey
@@ -30,11 +31,11 @@ def preparer_graphe(G):
def selectionner_niveaux():
"""Interface pour sélectionner les niveaux de départ et d'arrivée."""
- st.markdown("## Sélection des nœuds de départ et d'arrivée")
- valeur_defaut = "-- Sélectionner un niveau --"
+ st.markdown(f"## {str(_('pages.analyse.selection_nodes', 'Sélection des nœuds de départ et d\'arrivée'))}")
+ valeur_defaut = str(_("pages.analyse.select_level", "-- Sélectionner un niveau --"))
niveau_choix = [valeur_defaut] + list(niveau_labels.values())
- niveau_depart = st.selectbox("Niveau de départ", niveau_choix, key="analyse_niveau_depart")
+ niveau_depart = st.selectbox(str(_("pages.analyse.start_level", "Niveau de départ")), niveau_choix, key="analyse_niveau_depart")
if niveau_depart == valeur_defaut:
return None, None
@@ -42,7 +43,7 @@ def selectionner_niveaux():
niveaux_arrivee_possibles = [v for k, v in niveau_labels.items() if k > niveau_depart_int]
niveaux_arrivee_choix = [valeur_defaut] + niveaux_arrivee_possibles
- analyse_niveau_arrivee = st.selectbox("Niveau d'arrivée", niveaux_arrivee_choix, key="analyse_niveau_arrivee")
+ analyse_niveau_arrivee = st.selectbox(str(_("pages.analyse.end_level", "Niveau d'arrivée")), niveaux_arrivee_choix, key="analyse_niveau_arrivee")
if analyse_niveau_arrivee == valeur_defaut:
return niveau_depart_int, None
@@ -54,7 +55,7 @@ def selectionner_minerais(G, niveau_depart, niveau_arrivee):
"""Interface pour sélectionner les minerais si nécessaire."""
minerais_selection = None
if niveau_depart < 2 < niveau_arrivee:
- st.markdown("### Sélectionner un ou plusieurs minerais")
+ st.markdown(f"### {str(_('pages.analyse.select_minerals', 'Sélectionner un ou plusieurs minerais'))}")
# Tous les nœuds de niveau 2 (minerai)
minerais_nodes = sorted([
n for n, d in G.nodes(data=True)
@@ -62,7 +63,7 @@ def selectionner_minerais(G, niveau_depart, niveau_arrivee):
])
minerais_selection = st.multiselect(
- "Filtrer par minerais (optionnel)",
+ str(_("pages.analyse.filter_by_minerals", "Filtrer par minerais (optionnel)")),
minerais_nodes,
key="analyse_minerais"
)
@@ -73,15 +74,15 @@ def selectionner_minerais(G, niveau_depart, niveau_arrivee):
def selectionner_noeuds(G, niveaux_temp, niveau_depart, niveau_arrivee):
"""Interface pour sélectionner les nœuds spécifiques de départ et d'arrivée."""
st.markdown("---")
- st.markdown("## Sélection fine des items")
+ st.markdown(f"## {str(_('pages.analyse.fine_selection', 'Sélection fine des items'))}")
depart_nodes = [n for n in G.nodes() if niveaux_temp.get(n) == niveau_depart]
arrivee_nodes = [n for n in G.nodes() if niveaux_temp.get(n) == niveau_arrivee]
- noeuds_depart = st.multiselect("Filtrer par noeuds de départ (optionnel)",
+ noeuds_depart = st.multiselect(str(_("pages.analyse.filter_start_nodes", "Filtrer par noeuds de départ (optionnel)")),
sorted(depart_nodes),
key="analyse_noeuds_depart")
- noeuds_arrivee = st.multiselect("Filtrer par noeuds d'arrivée (optionnel)",
+ noeuds_arrivee = st.multiselect(str(_("pages.analyse.filter_end_nodes", "Filtrer par noeuds d'arrivée (optionnel)")),
sorted(arrivee_nodes),
key="analyse_noeuds_arrivee")
@@ -94,26 +95,26 @@ def selectionner_noeuds(G, niveaux_temp, niveau_depart, niveau_arrivee):
def configurer_filtres_vulnerabilite():
"""Interface pour configurer les filtres de vulnérabilité."""
st.markdown("---")
- st.markdown("## Sélection des filtres pour identifier les vulnérabilités")
+ st.markdown(f"## {str(_('pages.analyse.vulnerability_filters', 'Sélection des filtres pour identifier les vulnérabilités'))}")
- filtrer_ics = st.checkbox("Filtrer les chemins contenant au moins minerai critique pour un composant (ICS > 66 %)",
+ filtrer_ics = st.checkbox(str(_("pages.analyse.filter_ics", "Filtrer les chemins contenant au moins minerai critique pour un composant (ICS > 66 %)")),
key="analyse_filtrer_ics")
- filtrer_ivc = st.checkbox("Filtrer les chemins contenant au moins un minerai critique par rapport à la concurrence sectorielle (IVC > 30)",
+ filtrer_ivc = st.checkbox(str(_("pages.analyse.filter_ivc", "Filtrer les chemins contenant au moins un minerai critique par rapport à la concurrence sectorielle (IVC > 30)")),
key="analyse_filtrer_ivc")
- filtrer_ihh = st.checkbox("Filtrer les chemins contenant au moins une opération critique par rapport à la concentration géographique ou industrielle (IHH pays ou acteurs > 25)",
+ filtrer_ihh = st.checkbox(str(_("pages.analyse.filter_ihh", "Filtrer les chemins contenant au moins une opération critique par rapport à la concentration géographique ou industrielle (IHH pays ou acteurs > 25)")),
key="analyse_filtrer_ihh")
ihh_type = "Pays"
if filtrer_ihh:
- ihh_type = st.radio("Appliquer le filtre IHH sur :",
- ["Pays", "Acteurs"],
+ ihh_type = st.radio(str(_("pages.analyse.apply_ihh_filter", "Appliquer le filtre IHH sur :")),
+ [str(_("pages.analyse.countries", "Pays")), str(_("pages.analyse.actors", "Acteurs"))],
horizontal=True,
key="analyse_ihh_type")
- filtrer_isg = st.checkbox("Filtrer les chemins contenant un pays instable (ISG ≥ 60)",
+ filtrer_isg = st.checkbox(str(_("pages.analyse.filter_isg", "Filtrer les chemins contenant un pays instable (ISG ≥ 60)")),
key="analyse_filtrer_isg")
- logique_filtrage = st.radio("Logique de filtrage",
- ["OU", "ET"],
+ logique_filtrage = st.radio(str(_("pages.analyse.filter_logic", "Logique de filtrage")),
+ [str(_("pages.analyse.or", "OU")), str(_("pages.analyse.and", "ET"))],
horizontal=True,
key="analyse_logique_filtrage")
@@ -121,16 +122,16 @@ def configurer_filtres_vulnerabilite():
def interface_analyse(G_temp):
- st.markdown("# Analyse du graphe")
- with st.expander("Comment utiliser cet onglet ?", expanded=False):
- st.markdown("""
- 1. Sélectionnez le niveau de départ (produit final, composant ou minerai)
- 2. Choisissez le niveau d'arrivée souhaité
- 3. Affinez votre sélection en spécifiant soit un ou des minerais à cibler spécifiquement ou des items précis à chaque niveau (optionnel)
- 4. Définissez les critères d'analyse en sélectionnant les indices de vulnérabilité pertinents
- 5. Choisissez le mode de combinaison des indices (ET/OU) selon votre besoin d'analyse
- 6. Explorez le graphique généré en utilisant les contrôles de zoom et de déplacement ; vous pouvez basculer en mode plein écran pour le graphe
- """)
+ st.markdown(f"# {str(_('pages.analyse.title', 'Analyse du graphe'))}")
+ with st.expander(str(_("pages.analyse.help", "Comment utiliser cet onglet ?")), expanded=False):
+ st.markdown("\n".join(_("pages.analyse.help_content", [
+ "1. Sélectionnez le niveau de départ (produit final, composant ou minerai)",
+ "2. Choisissez le niveau d'arrivée souhaité",
+ "3. Affinez votre sélection en spécifiant soit un ou des minerais à cibler spécifiquement ou des items précis à chaque niveau (optionnel)",
+ "4. Définissez les critères d'analyse en sélectionnant les indices de vulnérabilité pertinents",
+ "5. Choisissez le mode de combinaison des indices (ET/OU) selon votre besoin d'analyse",
+ "6. Explorez le graphique généré en utilisant les contrôles de zoom et de déplacement ; vous pouvez basculer en mode plein écran pour le graphe"
+ ])))
st.markdown("---")
try:
@@ -154,7 +155,7 @@ def interface_analyse(G_temp):
# Lancement de l'analyse
st.markdown("---")
- if st.button("Lancer l'analyse", type="primary", key="analyse_lancer"):
+ if st.button(str(_("pages.analyse.run_analysis", "Lancer l'analyse")), type="primary", key="analyse_lancer"):
afficher_sankey(
G_temp,
niveau_depart=niveau_depart,
@@ -171,4 +172,4 @@ def interface_analyse(G_temp):
)
except Exception as e:
- st.error(f"Erreur de prévisualisation du graphe : {e}")
+ st.error(f"{str(_('errors.graph_preview_error', 'Erreur de prévisualisation du graphe :'))} {e}")
diff --git a/app/analyse/sankey.py b/app/analyse/sankey.py
index 671747c..6a2dec8 100644
--- a/app/analyse/sankey.py
+++ b/app/analyse/sankey.py
@@ -5,6 +5,7 @@ import plotly.graph_objects as go
import networkx as nx
import logging
import tempfile
+from utils.translations import _
from utils.graph_utils import (
extraire_chemins_depuis,
@@ -188,11 +189,11 @@ def edge_info(G, u, v):
"""Génère l'info-bulle pour un lien"""
data = G.get_edge_data(u, v)
if not data:
- return f"Relation : {u} → {v}"
+ return f"{str(_('pages.analyse.sankey.relation', 'Relation'))} : {u} → {v}"
if isinstance(data, dict) and all(isinstance(k, int) for k in data):
data = data[0]
base = [f"{k}: {v}" for k, v in data.items()]
- return f"Relation : {u} → {v} " + " ".join(base)
+ return f"{str(_('pages.analyse.sankey.relation', 'Relation'))} : {u} → {v} " + " ".join(base)
def preparer_donnees_sankey(G, liens_chemins, niveaux, chemins):
"""Prépare les données pour le graphique Sankey"""
@@ -272,7 +273,7 @@ def creer_graphique_sankey(G, niveaux, df_liens, sorted_nodes, customdata, link_
))
fig.update_layout(
- title_text="Hiérarchie filtrée par niveaux et noeuds",
+ title_text=str(_("pages.analyse.sankey.filtered_hierarchy", "Hiérarchie filtrée par niveaux et noeuds")),
paper_bgcolor="white",
plot_bgcolor="white"
)
@@ -302,7 +303,7 @@ def exporter_graphe_filtre(G, liens_chemins):
with open(dot_path, encoding="utf-8") as f:
st.download_button(
- label="Télécharger le fichier DOT filtré",
+ label=str(_("pages.analyse.sankey.download_dot", "Télécharger le fichier DOT filtré")),
data=f.read(),
file_name="graphe_filtré.dot",
mime="text/plain"
@@ -324,7 +325,7 @@ def afficher_sankey(
chemins = extraire_chemins_selon_criteres(G, niveaux, niveau_depart, noeuds_depart, noeuds_arrivee, minerais)
if not chemins:
- st.warning("Aucun chemin trouvé pour les critères spécifiés.")
+ st.warning(str(_("pages.analyse.sankey.no_paths", "Aucun chemin trouvé pour les critères spécifiés.")))
return
# Étape 3 : Filtrage des chemins selon les critères de vulnérabilité
@@ -334,7 +335,7 @@ def afficher_sankey(
)
if not liens_chemins:
- st.warning("Aucun chemin ne correspond aux critères.")
+ st.warning(str(_("pages.analyse.sankey.no_matching_paths", "Aucun chemin ne correspond aux critères.")))
return
# Étape 4 : Préparation des données pour le graphique Sankey
diff --git a/app/fiches/interface.py b/app/fiches/interface.py
index 7e9d8fb..eebc042 100644
--- a/app/fiches/interface.py
+++ b/app/fiches/interface.py
@@ -3,6 +3,7 @@ import streamlit as st
import requests
import os
import pathlib
+from utils.translations import _
from .utils.tickets.display import afficher_tickets_par_fiche
from .utils.tickets.creation import formulaire_creation_ticket_dynamique
@@ -17,21 +18,23 @@ from .utils.fiche_utils import load_seuils, doit_regenerer_fiche
from .generer import generer_fiche
def interface_fiches():
- st.markdown("# Découverte des fiches")
- with st.expander("Comment utiliser cet onglet ?", expanded=False):
- st.markdown("""
- 1. Parcourez la liste des fiches disponibles par catégorie
- 2. Sélectionnez une fiche pour afficher son contenu complet
- 3. Consultez les données détaillées, graphiques et analyses supplémentaires
- 4. Utilisez ces informations pour approfondir votre compréhension des vulnérabilités identifiées
-
- Les catégories sont les suivantes :
- * Assemblage : opération d'assemblage des produits finaux à partir des composants
- * Connexe : opérations diverses nécessaires pour fabriquer le numérique, mais n'entrant pas directement dans sa composition
- * Criticités : indices utilisés pour identifier et évaluer les vulnérabilités
- * Fabrication : opération de fabrication des composants à partir de minerais
- * Minerai : description et opérations d'extraction et de traitement des minerais
- """)
+ st.markdown(f"# {str(_('pages.fiches.title', 'Découverte des fiches'))}")
+ with st.expander(str(_("pages.fiches.help", "Comment utiliser cet onglet ?")), expanded=False):
+ st.markdown("\n".join([
+ " " + line for line in _("pages.fiches.help_content", [
+ "1. Parcourez la liste des fiches disponibles par catégorie",
+ "2. Sélectionnez une fiche pour afficher son contenu complet",
+ "3. Consultez les données détaillées, graphiques et analyses supplémentaires",
+ "4. Utilisez ces informations pour approfondir votre compréhension des vulnérabilités identifiées",
+ "",
+ "Les catégories sont les suivantes :",
+ "* Assemblage : opération d'assemblage des produits finaux à partir des composants",
+ "* Connexe : opérations diverses nécessaires pour fabriquer le numérique, mais n'entrant pas directement dans sa composition",
+ "* Criticités : indices utilisés pour identifier et évaluer les vulnérabilités",
+ "* Fabrication : opération de fabrication des composants à partir de minerais",
+ "* Minerai : description et opérations d'extraction et de traitement des minerais"
+ ])
+ ]))
st.markdown("---")
if "fiches_arbo" not in st.session_state:
@@ -39,18 +42,24 @@ def interface_fiches():
arbo = st.session_state.get("fiches_arbo", {})
if not arbo:
- st.warning("Aucune fiche disponible pour le moment.")
+ st.warning(str(_("pages.fiches.no_files", "Aucune fiche disponible pour le moment.")))
return
dossiers = sorted(arbo.keys(), key=lambda x: x.lower())
- dossier_choisi = st.selectbox("Choisissez une catégorie de fiches", ["-- Sélectionner un dossier --"] + dossiers)
+ dossier_choisi = st.selectbox(
+ str(_("pages.fiches.choose_category", "Choisissez une catégorie de fiches")),
+ [str(_("pages.fiches.select_folder", "-- Sélectionner un dossier --"))] + dossiers
+ )
- if dossier_choisi and dossier_choisi != "-- Sélectionner un dossier --":
+ if dossier_choisi and dossier_choisi != str(_("pages.fiches.select_folder", "-- Sélectionner un dossier --")):
fiches = arbo.get(dossier_choisi, [])
noms_fiches = [f['nom'] for f in fiches]
- fiche_choisie = st.selectbox("Choisissez une fiche", ["-- Sélectionner une fiche --"] + noms_fiches)
+ fiche_choisie = st.selectbox(
+ str(_("pages.fiches.choose_file", "Choisissez une fiche")),
+ [str(_("pages.fiches.select_file", "-- Sélectionner une fiche --"))] + noms_fiches
+ )
- if fiche_choisie and fiche_choisie != "-- Sélectionner une fiche --":
+ if fiche_choisie and fiche_choisie != str(_("pages.fiches.select_file", "-- Sélectionner une fiche --")):
fiche_info = next((f for f in fiches if f["nom"] == fiche_choisie), None)
if fiche_info:
try:
@@ -92,19 +101,19 @@ def interface_fiches():
if os.path.exists(pdf_path):
with open(pdf_path, "rb") as pdf_file:
st.download_button(
- label="Télécharger cette fiche en PDF",
+ label=str(_("pages.fiches.download_pdf", "Télécharger cette fiche en PDF")),
data=pdf_file,
file_name=pdf_name,
mime="application/pdf",
- help="Télécharger la version PDF de cette fiche",
+ help=str(_("pages.fiches.download_pdf", "Télécharger cette fiche en PDF")),
key="telecharger_fiche_pdf"
)
else:
- st.warning("Le fichier PDF de cette fiche n'est pas disponible.")
+ st.warning(str(_("pages.fiches.pdf_unavailable", "Le fichier PDF de cette fiche n'est pas disponible.")))
- st.markdown("## Gestion des tickets pour cette fiche")
+ st.markdown(f"## {str(_('pages.fiches.ticket_management', 'Gestion des tickets pour cette fiche'))}")
afficher_tickets_par_fiche(rechercher_tickets_gitea(fiche_choisie))
formulaire_creation_ticket_dynamique(fiche_choisie)
except Exception as e:
- st.error(f"Erreur lors du chargement de la fiche : {e}")
+ st.error(f"{str(_('pages.fiches.loading_error', 'Erreur lors du chargement de la fiche :'))} {e}")
diff --git a/app/fiches/utils/tickets/core.py b/app/fiches/utils/tickets/core.py
index 99a88e5..26f2b69 100644
--- a/app/fiches/utils/tickets/core.py
+++ b/app/fiches/utils/tickets/core.py
@@ -5,6 +5,7 @@ import json
import requests
import os
import streamlit as st
+from utils.translations import _
from config import GITEA_URL, GITEA_TOKEN, ORGANISATION, DEPOT_FICHES, ENV
@@ -16,7 +17,7 @@ def gitea_request(method, url, **kwargs):
response.raise_for_status()
return response
except requests.RequestException as e:
- st.error(f"Erreur Gitea ({method.upper()}): {e}")
+ st.error(f"{str(_('errors.gitea_error', 'Erreur Gitea'))} ({method.upper()}): {e}")
return None
@@ -38,9 +39,9 @@ def charger_fiches_et_labels():
"item": item.strip()
}
except FileNotFoundError:
- st.error(f"❌ Le fichier {chemin_csv} est introuvable.")
+ st.error(f"❌ {str(_('errors.file_not_found', 'Le fichier'))} {chemin_csv} {str(_('errors.is_missing', 'est introuvable.'))}")
except Exception as e:
- st.error(f"❌ Erreur lors du chargement des fiches : {str(e)}")
+ st.error(f"❌ {str(_('errors.file_loading', 'Erreur lors du chargement des fiches :'))} {str(e)}")
return dictionnaire_fiches
@@ -56,7 +57,7 @@ def rechercher_tickets_gitea(fiche_selectionnee):
try:
issues = reponse.json()
except Exception as e:
- st.error(f"Erreur de décodage JSON : {e}")
+ st.error(f"{str(_('errors.json_decode', 'Erreur de décodage JSON :'))} {e}")
return []
correspondances = charger_fiches_et_labels()
@@ -86,7 +87,7 @@ def get_labels_existants():
try:
return {label['name']: label['id'] for label in reponse.json()}
except Exception as e:
- st.error(f"Erreur de parsing des labels : {e}")
+ st.error(f"{str(_('errors.label_parsing', 'Erreur de parsing des labels :'))} {e}")
return {}
@@ -113,6 +114,6 @@ def creer_ticket_gitea(titre, corps, labels):
issue_url = reponse.json().get("html_url", "")
if issue_url:
- st.success(f"Ticket créé ! [Voir le ticket]({issue_url})")
+ st.success(f"{str(_('pages.fiches.tickets.created_success', 'Ticket créé !'))} [Voir le ticket]({issue_url})")
else:
- st.success("Ticket créé avec succès.")
+ st.success(str(_('pages.fiches.tickets.created', 'Ticket créé avec succès.')))
diff --git a/app/fiches/utils/tickets/creation.py b/app/fiches/utils/tickets/creation.py
index eed99a5..0035a27 100644
--- a/app/fiches/utils/tickets/creation.py
+++ b/app/fiches/utils/tickets/creation.py
@@ -3,6 +3,7 @@
import re
import base64
import streamlit as st
+from utils.translations import _
from .core import charger_fiches_et_labels, construire_corps_ticket_markdown, creer_ticket_gitea, get_labels_existants, nettoyer_labels
from config import ENV
import requests
@@ -38,7 +39,7 @@ def generer_labels(fiche_selectionnee):
if len(cible["operations"]) == 1:
labels.append(cible["operations"][0])
elif len(cible["operations"]) > 1:
- selected_ops = st.multiselect("Labels opération à associer",
+ selected_ops = st.multiselect(str(_("pages.fiches.tickets.contribution_type", "Labels opération à associer")),
cible["operations"],
default=cible["operations"])
@@ -52,14 +53,14 @@ def creer_champs_formulaire(sections, fiche_selectionnee):
for section, aide in sections.items():
if "Type de contribution" in section:
options = sorted(set(re.findall(r"- \[.\] (.+)", aide)))
- if "Autre" not in options:
- options.append("Autre")
- choix = st.radio("Type de contribution", options)
- reponses[section] = st.text_input("Précisez", "") if choix == "Autre" else choix
+ if str(_("pages.fiches.tickets.other", "Autre")) not in options:
+ options.append(str(_("pages.fiches.tickets.other", "Autre")))
+ choix = st.radio(str(_("pages.fiches.tickets.contribution_type", "Type de contribution")), options)
+ reponses[section] = st.text_input(str(_("pages.fiches.tickets.specify", "Précisez")), "") if choix == str(_("pages.fiches.tickets.other", "Autre")) else choix
elif "Fiche concernée" in section:
url_fiche = f"https://fabnum-git.peccini.fr/FabNum/Fiches/src/branch/{ENV}/Documents/{fiche_selectionnee.replace(' ', '%20')}"
reponses[section] = url_fiche
- st.text_input("Fiche concernée", value=url_fiche, disabled=True)
+ st.text_input(str(_("pages.fiches.tickets.concerned_card", "Fiche concernée")), value=url_fiche, disabled=True)
elif "Sujet de la proposition" in section:
reponses[section] = st.text_input(section, help=aide)
else:
@@ -71,9 +72,9 @@ def creer_champs_formulaire(sections, fiche_selectionnee):
def afficher_controles_formulaire():
"""Affiche les boutons de contrôle du formulaire."""
col1, col2 = st.columns(2)
- if col1.button("Prévisualiser le ticket"):
+ if col1.button(str(_("pages.fiches.tickets.preview", "Prévisualiser le ticket"))):
st.session_state.previsualiser = True
- if col2.button("Annuler"):
+ if col2.button(str(_("pages.fiches.tickets.cancel", "Annuler"))):
st.session_state.previsualiser = False
st.rerun()
@@ -83,7 +84,7 @@ def gerer_previsualisation_et_soumission(reponses, labels, selected_ops, cible):
if not st.session_state.get("previsualiser", False):
return
- st.subheader("Prévisualisation du ticket")
+ st.subheader(str(_("pages.fiches.tickets.preview_title", "Prévisualisation du ticket")))
for section, texte in reponses.items():
st.markdown(f"#### {section}")
st.code(texte, language="markdown")
@@ -91,9 +92,9 @@ def gerer_previsualisation_et_soumission(reponses, labels, selected_ops, cible):
titre_ticket = reponses.get("Sujet de la proposition", "").strip() or "Ticket FabNum"
final_labels = nettoyer_labels(labels + selected_ops + ([cible["item"]] if cible else []))
- st.markdown(f"**Résumé :**\n- **Titre** : `{titre_ticket}`\n- **Labels** : `{', '.join(final_labels)}`")
+ st.markdown(f"**{str(_('pages.fiches.tickets.summary', 'Résumé'))} :**\n- **{str(_('pages.fiches.tickets.title', 'Titre'))}** : `{titre_ticket}`\n- **{str(_('pages.fiches.tickets.labels', 'Labels'))}** : `{', '.join(final_labels)}`")
- if st.button("Confirmer la création du ticket"):
+ if st.button(str(_("pages.fiches.tickets.confirm", "Confirmer la création du ticket"))):
labels_existants = get_labels_existants()
labels_ids = [labels_existants[l] for l in final_labels if l in labels_existants]
if "Backlog" in labels_existants:
@@ -103,16 +104,16 @@ def gerer_previsualisation_et_soumission(reponses, labels, selected_ops, cible):
creer_ticket_gitea(titre_ticket, corps, labels_ids)
st.session_state.previsualiser = False
- st.success("Ticket créé et formulaire vidé.")
+ st.success(str(_("pages.fiches.tickets.created", "Ticket créé et formulaire vidé.")))
def formulaire_creation_ticket_dynamique(fiche_selectionnee):
"""Fonction principale pour le formulaire de création de ticket."""
- with st.expander("Créer un nouveau ticket lié à cette fiche", expanded=False):
+ with st.expander(str(_("pages.fiches.tickets.create_new", "Créer un nouveau ticket lié à cette fiche")), expanded=False):
# Chargement et vérification du modèle
contenu_modele = charger_modele_ticket()
if not contenu_modele:
- st.error("Impossible de charger le modèle de ticket.")
+ st.error(str(_("pages.fiches.tickets.model_load_error", "Impossible de charger le modèle de ticket.")))
return
# Traitement du modèle et génération du formulaire
@@ -135,5 +136,5 @@ def charger_modele_ticket():
r.raise_for_status()
return base64.b64decode(r.json().get("content", "")).decode("utf-8")
except Exception as e:
- st.error(f"Erreur chargement modèle : {e}")
+ st.error(f"{str(_('pages.fiches.tickets.model_error', 'Erreur chargement modèle :'))} {e}")
return ""
diff --git a/app/fiches/utils/tickets/display.py b/app/fiches/utils/tickets/display.py
index 16b3573..773921c 100644
--- a/app/fiches/utils/tickets/display.py
+++ b/app/fiches/utils/tickets/display.py
@@ -5,23 +5,28 @@ import html
import re
from collections import defaultdict
from dateutil import parser
+from utils.translations import _
from .core import rechercher_tickets_gitea
def extraire_statut_par_label(ticket):
labels = [label.get('name', '') for label in ticket.get('labels', [])]
- for statut in ["Backlog", "En attente de traitement", "En cours", "Terminés", "Non retenus"]:
+ for statut in ["Backlog",
+ str(_("pages.fiches.tickets.status.awaiting", "En attente de traitement")),
+ str(_("pages.fiches.tickets.status.in_progress", "En cours")),
+ str(_("pages.fiches.tickets.status.completed", "Terminés")),
+ str(_("pages.fiches.tickets.status.rejected", "Non retenus"))]:
if statut in labels:
return statut
- return "Autres"
+ return str(_("pages.fiches.tickets.status.others", "Autres"))
def afficher_tickets_par_fiche(tickets):
if not tickets:
- st.info("Aucun ticket lié à cette fiche.")
+ st.info(str(_("pages.fiches.tickets.no_linked_tickets", "Aucun ticket lié à cette fiche.")))
return
- st.markdown("**Tickets associés à cette fiche**")
+ st.markdown(str(_("pages.fiches.tickets.associated_tickets", "**Tickets associés à cette fiche**")))
tickets_groupes = defaultdict(list)
for ticket in tickets:
statut = extraire_statut_par_label(ticket)
@@ -29,9 +34,15 @@ def afficher_tickets_par_fiche(tickets):
nb_backlogs = len(tickets_groupes["Backlog"])
if nb_backlogs:
- st.info(f"⤇ {nb_backlogs} ticket(s) en attente de modération ne sont pas affichés.")
+ st.info(f"⤇ {nb_backlogs} {str(_('pages.fiches.tickets.moderation_notice', 'ticket(s) en attente de modération ne sont pas affichés.'))}")
- ordre_statuts = ["En attente de traitement", "En cours", "Terminés", "Non retenus", "Autres"]
+ ordre_statuts = [
+ str(_("pages.fiches.tickets.status.awaiting", "En attente de traitement")),
+ str(_("pages.fiches.tickets.status.in_progress", "En cours")),
+ str(_("pages.fiches.tickets.status.completed", "Terminés")),
+ str(_("pages.fiches.tickets.status.rejected", "Non retenus")),
+ str(_("pages.fiches.tickets.status.others", "Autres"))
+ ]
for statut in ordre_statuts:
if tickets_groupes[statut]:
with st.expander(f"{statut} ({len(tickets_groupes[statut])})", expanded=(statut == "En cours")):
@@ -50,14 +61,14 @@ def recuperer_commentaires_ticket(issue_index):
response.raise_for_status()
return response.json()
except Exception as e:
- st.error(f"Erreur lors de la récupération des commentaires : {e}")
+ st.error(f"{str(_('pages.fiches.tickets.comment_error', 'Erreur lors de la récupération des commentaires :'))} {e}")
return []
def afficher_carte_ticket(ticket):
- titre = ticket.get("title", "Sans titre")
+ titre = ticket.get("title", str(_("pages.fiches.tickets.no_title", "Sans titre")))
url = ticket.get("html_url", "")
- user = ticket.get("user", {}).get("login", "inconnu")
+ user = ticket.get("user", {}).get("login", str(_("pages.fiches.tickets.unknown", "inconnu")))
created = ticket.get("created_at", "")
updated = ticket.get("updated_at", "")
body = ticket.get("body", "")
@@ -75,12 +86,12 @@ def afficher_carte_ticket(ticket):
return "?"
date_created_str = format_date(created)
- maj_info = f"(MAJ {format_date(updated)})" if updated and updated != created else ""
+ maj_info = f"({str(_('pages.fiches.tickets.updated', 'MAJ'))} {format_date(updated)})" if updated and updated != created else ""
commentaires = recuperer_commentaires_ticket(ticket.get("number"))
commentaires_html = ""
for commentaire in commentaires:
- auteur = html.escape(commentaire.get('user', {}).get('login', 'inconnu'))
+ auteur = html.escape(commentaire.get('user', {}).get('login', str(_("pages.fiches.tickets.unknown", "inconnu"))))
contenu = html.escape(commentaire.get('body', ''))
date = format_date(commentaire.get('created_at', ''))
commentaires_html += f"""
@@ -94,12 +105,12 @@ def afficher_carte_ticket(ticket):
st.markdown(f"""
""")
- st.sidebar.markdown(f"Connecté en tant que `{st.session_state.username}`")
- if st.sidebar.button("Se déconnecter"):
+ st.sidebar.markdown(f"{str(_('auth.logged_as', 'Connecté en tant que'))} `{st.session_state.username}`")
+ if st.sidebar.button(str(_("auth.logout", "Se déconnecter"))):
st.session_state.logged_in = False
st.session_state.username = ""
st.session_state.token = ""
- st.success("Déconnecté avec succès.")
+ st.success(str(_("auth.success", "Déconnecté avec succès.")))
st.rerun()
st.html("""
- """)
+ """)
\ No newline at end of file
diff --git a/components/footer.py b/components/footer.py
index b15af81..c512ac8 100644
--- a/components/footer.py
+++ b/components/footer.py
@@ -1,4 +1,5 @@
import streamlit as st
+from utils.translations import _
def afficher_pied_de_page():
@@ -6,15 +7,15 @@ def afficher_pied_de_page():
""", unsafe_allow_html=True)
- st.markdown("""
+ st.markdown(f"""
diff --git a/components/header.py b/components/header.py
index 0c3c802..560b92b 100644
--- a/components/header.py
+++ b/components/header.py
@@ -1,18 +1,19 @@
import streamlit as st
from config import ENV
+from utils.translations import _
def afficher_entete():
- header = """
+ header = f"""
-
FabNum - Chaîne de fabrication du numérique
+
{_("header.title", "FabNum - Chaîne de fabrication du numérique")}
"""
if ENV == "dev":
- header += "
🔧 Vous êtes dans l'environnement de développement.
"
+ header += f"
🔧 {_("app.dev_mode", "Vous êtes dans l'environnement de développement.")}
"
else:
- header += "
Parcours de l'écosystème et identification des vulnérabilités.
"
+ header += f"
{_("header.subtitle", "Parcours de l'écosystème et identification des vulnérabilités.")}
"
header += """
diff --git a/components/sidebar.py b/components/sidebar.py
index 5a45ba4..7f09212 100644
--- a/components/sidebar.py
+++ b/components/sidebar.py
@@ -1,26 +1,35 @@
import streamlit as st
from components.connexion import connexion, bouton_deconnexion
import streamlit.components.v1 as components
+from utils.translations import _
def afficher_menu():
with st.sidebar:
- st.markdown("""
-