From 7ad19ac7f4d0f6895a59c637b06ef947e238b5fa Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 17 May 2026 00:42:45 +0000 Subject: [PATCH] feat(nav,i18n): sticky footer with Back-to-Home + localized tool headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two unrelated UX issues addressed in one sweep across all nine tool pages because they share the same edit surface. (1) Sticky footer replaces the top + bottom back-link buttons. Reported: a big white empty footer space at the bottom of every page; the Back to Home button at the top scrolled out of view on long pages. New ``render_sticky_footer()`` helper in ``components/_legacy.py`` injects a fixed-position bar at ``bottom: 0`` of the viewport with: - A border-top so it visually reads as a non-movable bar. - A semi-transparent background (rgba 0.96 + ``backdrop-filter: blur``) so content underneath shows through faintly when the user scrolls. - A styled ```` anchor (not an ``st.button``) because Streamlit widgets can't be CSS-positioned reliably — Streamlit owns the widget's DOM container and re-mounts it on every rerun. A real anchor sits exactly where the CSS puts it and triggers Streamlit's URL routing to the home page. - ``padding-bottom: 3.5rem`` on the main container so the last widget isn't hidden behind the bar. Called once per tool page, immediately after ``hide_streamlit_chrome()`` so it renders even on pages that ``st.stop()`` early before any other content runs. The old top-and-bottom ``back_to_home_link()`` calls are removed from every tool page; their entry/exit points were dropping the button when the script short-circuited. (2) Tool-page headers now localize. Reported: switching the sidebar language picker to Spanish left the tool page's title + caption in English. Root cause: every page had hard-coded ``st.title("✂️ Clean Text")`` / ``st.caption("Trim whitespace...")`` strings. Added per-tool ``tools..page_title`` and ``tools..page_caption`` keys to ``en.json`` and ``es.json`` for all nine tools. Routed each page's title/caption call through ``t()``. Verified: with ``ui_lang=es`` set, the Clean Text page now renders "✂️ Limpiar texto" + the Spanish caption. Updated ``tests/gui/test_smoke.py::EXPECTED_SUBSTRINGS`` so the ``es`` column for each tool page asserts the actual Spanish string (was a duplicate of the English string back when the page bodies were English-only). 2220 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/gui/components/__init__.py | 1 + src/gui/components/_legacy.py | 82 ++++++++++++++++++++++++++ src/gui/pages/1_Deduplicator.py | 10 ++-- src/gui/pages/2_Text_Cleaner.py | 13 ++-- src/gui/pages/3_Format_Standardizer.py | 14 ++--- src/gui/pages/4_Missing_Values.py | 13 ++-- src/gui/pages/5_Column_Mapper.py | 13 ++-- src/gui/pages/6_Outlier_Detector.py | 11 ++-- src/gui/pages/7_Multi_File_Merger.py | 11 ++-- src/gui/pages/8_Validator_Reporter.py | 11 ++-- src/gui/pages/9_Pipeline_Runner.py | 13 ++-- src/i18n/packs/en.json | 36 ++++++++--- src/i18n/packs/es.json | 36 ++++++++--- tests/gui/test_smoke.py | 18 +++--- 14 files changed, 194 insertions(+), 88 deletions(-) diff --git a/src/gui/components/__init__.py b/src/gui/components/__init__.py index e66f83b..dbe64cc 100644 --- a/src/gui/components/__init__.py +++ b/src/gui/components/__init__.py @@ -47,6 +47,7 @@ from .activation import ( # noqa: F401 re-exported __all__ = [ # Shared chrome / pickup "back_to_home_link", + "render_sticky_footer", "hide_streamlit_chrome", "html_download_button", "local_download_button", diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py index d5ddc3a..3516746 100644 --- a/src/gui/components/_legacy.py +++ b/src/gui/components/_legacy.py @@ -491,6 +491,88 @@ def local_download_button( html_download_button = local_download_button +def render_sticky_footer() -> None: + """Render a slim fixed-position footer at the bottom of the viewport. + + Contains a "Back to Home" link that's always visible regardless of + scroll position. Replaces the previous top + bottom-of-page + ``back_to_home_link`` buttons: a single sticky bar is more + discoverable, doesn't scroll out of view, and visually reads as a + "non-movable footer" (border-top + slim padding) rather than + just-another-button-at-the-bottom. + + Implementation notes: + + - Uses a styled ```` anchor rather than an + ``st.button`` because Streamlit widgets can't be CSS-positioned + reliably (their DOM container is owned by Streamlit's renderer + and gets re-mounted on every rerun). A real anchor sits anywhere + we want and triggers Streamlit's URL routing to the home page. + - ``href="home"`` is a relative path so it works behind a reverse + proxy / non-root mount. + - We bump ``padding-bottom`` on the main container so the last + widget isn't hidden behind the fixed bar. + - Theme: ``rgba(255,255,255,0.96)`` background with a thin + semi-transparent border-top. The blur backdrop keeps content + slightly visible underneath. Works against both light and dark + Streamlit themes — explicit colours, not theme variables, + because Streamlit's CSS variable surface is unstable across + minor versions. + """ + import html as _html + label = _html.escape(_t("nav.back_to_home")) + st.markdown( + f""" + + +""", + unsafe_allow_html=True, + ) + + def back_to_home_link(*, key: str = "_back_to_home_link") -> None: """Render a "← Back to Home" affordance on a tool page. diff --git a/src/gui/pages/1_Deduplicator.py b/src/gui/pages/1_Deduplicator.py index aa0ae47..05716dc 100644 --- a/src/gui/pages/1_Deduplicator.py +++ b/src/gui/pages/1_Deduplicator.py @@ -19,6 +19,7 @@ from src.core.io import read_file, list_sheets, detect_encoding, detect_delimite from src.gui.components import ( apply_review_decisions, back_to_home_link, + render_sticky_footer, config_panel, hide_streamlit_chrome, html_download_button, @@ -27,10 +28,11 @@ from src.gui.components import ( require_feature_or_render_upgrade, results_summary, ) +from src.i18n import t from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.DEDUPLICATOR) # --------------------------------------------------------------------------- @@ -55,8 +57,8 @@ for key, default in _DEFAULTS.items(): # Header # --------------------------------------------------------------------------- -st.title("🔍 Find Duplicates") -st.caption("Find and remove duplicate rows in CSV, delimited text, and Excel files.") +st.title(t("tools.01_deduplicator.page_title")) +st.caption(t("tools.01_deduplicator.page_caption")) # --------------------------------------------------------------------------- @@ -399,8 +401,6 @@ else: # Footer # --------------------------------------------------------------------------- -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption( "Runs locally. Your data never leaves this computer. " diff --git a/src/gui/pages/2_Text_Cleaner.py b/src/gui/pages/2_Text_Cleaner.py index 972daad..e808668 100644 --- a/src/gui/pages/2_Text_Cleaner.py +++ b/src/gui/pages/2_Text_Cleaner.py @@ -16,12 +16,14 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, html_download_button, pickup_or_upload, render_hidden_aware_preview, require_feature_or_render_upgrade, ) +from src.i18n import t from src.license import FeatureFlag from src.core.text_clean import ( PRESETS, @@ -32,7 +34,7 @@ from src.core.text_clean import ( ) hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.TEXT_CLEANER) @@ -40,11 +42,8 @@ require_feature_or_render_upgrade(FeatureFlag.TEXT_CLEANER) # Header # --------------------------------------------------------------------------- -st.title("✂️ Clean Text") -st.caption( - "Trim whitespace, fold smart quotes, strip invisible characters, and " - "normalize line endings. Runs locally — your data never leaves this computer." -) +st.title(t("tools.02_text_cleaner.page_title")) +st.caption(t("tools.02_text_cleaner.page_caption")) # --------------------------------------------------------------------------- # File upload @@ -379,8 +378,6 @@ with dl_c: mime="application/json", ) -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") diff --git a/src/gui/pages/3_Format_Standardizer.py b/src/gui/pages/3_Format_Standardizer.py index 7386167..c043f95 100644 --- a/src/gui/pages/3_Format_Standardizer.py +++ b/src/gui/pages/3_Format_Standardizer.py @@ -16,11 +16,13 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, html_download_button, pickup_or_upload, require_feature_or_render_upgrade, ) +from src.i18n import t from src.core.format_standardize import ( PRESETS, FieldType, @@ -30,7 +32,7 @@ from src.core.format_standardize import ( from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.FORMAT_STANDARDIZER) @@ -38,12 +40,8 @@ require_feature_or_render_upgrade(FeatureFlag.FORMAT_STANDARDIZER) # Header # --------------------------------------------------------------------------- -st.title("📐 Standardize Formats") -st.caption( - "Canonicalize dates, phone numbers, currency, names, addresses, and " - "booleans on a per-column basis. Runs locally — your data never leaves " - "this computer." -) +st.title(t("tools.03_format_standardizer.page_title")) +st.caption(t("tools.03_format_standardizer.page_caption")) # --------------------------------------------------------------------------- @@ -650,8 +648,6 @@ with dl_c: mime="application/json", ) -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") diff --git a/src/gui/pages/4_Missing_Values.py b/src/gui/pages/4_Missing_Values.py index 44470af..0bb7d75 100644 --- a/src/gui/pages/4_Missing_Values.py +++ b/src/gui/pages/4_Missing_Values.py @@ -16,11 +16,13 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, html_download_button, pickup_or_upload, require_feature_or_render_upgrade, ) +from src.i18n import t from src.core.missing import ( DEFAULT_SENTINELS, MissingOptions, @@ -31,7 +33,7 @@ from src.core.missing import ( from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.MISSING_HANDLER) @@ -39,11 +41,8 @@ require_feature_or_render_upgrade(FeatureFlag.MISSING_HANDLER) # Header # --------------------------------------------------------------------------- -st.title("🕳️ Fix Missing Values") -st.caption( - "Detect disguised nulls, profile missingness, and apply imputation or " - "drop strategies. Runs locally — your data never leaves this computer." -) +st.title(t("tools.04_missing_handler.page_title")) +st.caption(t("tools.04_missing_handler.page_caption")) # --------------------------------------------------------------------------- @@ -412,8 +411,6 @@ with dl_c: mime="application/json", ) -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") diff --git a/src/gui/pages/5_Column_Mapper.py b/src/gui/pages/5_Column_Mapper.py index 58eee55..52e0636 100644 --- a/src/gui/pages/5_Column_Mapper.py +++ b/src/gui/pages/5_Column_Mapper.py @@ -16,11 +16,13 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, html_download_button, pickup_or_upload, require_feature_or_render_upgrade, ) +from src.i18n import t from src.core.column_mapper import ( MapOptions, PRESETS, @@ -32,7 +34,7 @@ from src.core.column_mapper import ( from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.COLUMN_MAPPER) @@ -40,11 +42,8 @@ require_feature_or_render_upgrade(FeatureFlag.COLUMN_MAPPER) # Header # --------------------------------------------------------------------------- -st.title("🗂️ Map Columns") -st.caption( - "Rename columns, enforce a target schema, and coerce types. Runs locally — " - "your data never leaves this computer." -) +st.title(t("tools.05_column_mapper.page_title")) +st.caption(t("tools.05_column_mapper.page_caption")) # --------------------------------------------------------------------------- @@ -456,8 +455,6 @@ with dl_c: mime="application/json", ) -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") diff --git a/src/gui/pages/6_Outlier_Detector.py b/src/gui/pages/6_Outlier_Detector.py index 18a1391..033e921 100644 --- a/src/gui/pages/6_Outlier_Detector.py +++ b/src/gui/pages/6_Outlier_Detector.py @@ -13,21 +13,23 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, require_feature_or_render_upgrade, ) +from src.i18n import t from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.OUTLIER_DETECTOR) # --------------------------------------------------------------------------- # Header # --------------------------------------------------------------------------- -st.title("📊 Find Unusual Values") -st.caption("Detect and handle outliers in numeric columns.") +st.title(t("tools.06_outlier_detector.page_title")) +st.caption(t("tools.06_outlier_detector.page_caption")) st.info("This tool is under development.") @@ -96,10 +98,9 @@ st.button("Detect Outliers", type="primary", use_container_width=True, disabled= # Footer # --------------------------------------------------------------------------- -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption( "Runs locally. Your data never leaves this computer. " "| DataTools v3.0" ) + diff --git a/src/gui/pages/7_Multi_File_Merger.py b/src/gui/pages/7_Multi_File_Merger.py index d84075f..f4c6616 100644 --- a/src/gui/pages/7_Multi_File_Merger.py +++ b/src/gui/pages/7_Multi_File_Merger.py @@ -13,21 +13,23 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, require_feature_or_render_upgrade, ) +from src.i18n import t from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.MULTI_FILE_MERGER) # --------------------------------------------------------------------------- # Header # --------------------------------------------------------------------------- -st.title("📎 Combine Files") -st.caption("Combine multiple CSV and Excel files into one dataset.") +st.title(t("tools.07_multi_file_merger.page_title")) +st.caption(t("tools.07_multi_file_merger.page_caption")) st.info("This tool is under development.") @@ -94,10 +96,9 @@ st.button("Merge Files", type="primary", use_container_width=True, disabled=True # Footer # --------------------------------------------------------------------------- -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption( "Runs locally. Your data never leaves this computer. " "| DataTools v3.0" ) + diff --git a/src/gui/pages/8_Validator_Reporter.py b/src/gui/pages/8_Validator_Reporter.py index 43d891c..184a171 100644 --- a/src/gui/pages/8_Validator_Reporter.py +++ b/src/gui/pages/8_Validator_Reporter.py @@ -13,21 +13,23 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, require_feature_or_render_upgrade, ) +from src.i18n import t from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.VALIDATOR_REPORTER) # --------------------------------------------------------------------------- # Header # --------------------------------------------------------------------------- -st.title("✅ Quality Check") -st.caption("Validate data against rules and generate quality reports.") +st.title(t("tools.08_validator_reporter.page_title")) +st.caption(t("tools.08_validator_reporter.page_caption")) st.info("This tool is under development.") @@ -101,10 +103,9 @@ st.button("Validate & Generate Report", type="primary", use_container_width=True # Footer # --------------------------------------------------------------------------- -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption( "Runs locally. Your data never leaves this computer. " "| DataTools v3.0" ) + diff --git a/src/gui/pages/9_Pipeline_Runner.py b/src/gui/pages/9_Pipeline_Runner.py index 8d2bce8..d954c5a 100644 --- a/src/gui/pages/9_Pipeline_Runner.py +++ b/src/gui/pages/9_Pipeline_Runner.py @@ -16,11 +16,13 @@ if str(_project_root) not in sys.path: from src.gui.components import ( back_to_home_link, + render_sticky_footer, hide_streamlit_chrome, html_download_button, pickup_or_upload, require_feature_or_render_upgrade, ) +from src.i18n import t from src.core.pipeline import ( Pipeline, SOFT_DEPENDENCIES, @@ -33,7 +35,7 @@ from src.core.pipeline import ( from src.license import FeatureFlag hide_streamlit_chrome() -back_to_home_link() +render_sticky_footer() require_feature_or_render_upgrade(FeatureFlag.PIPELINE_RUNNER) @@ -41,11 +43,8 @@ require_feature_or_render_upgrade(FeatureFlag.PIPELINE_RUNNER) # Header # --------------------------------------------------------------------------- -st.title("⚙️ Automated Workflows") -st.caption( - "Chain DataTools cleaning steps into one repeatable workflow. The " - "pipeline recommends an order; you stay in control." -) +st.title(t("tools.09_pipeline_runner.page_title")) +st.caption(t("tools.09_pipeline_runner.page_caption")) # --------------------------------------------------------------------------- @@ -414,8 +413,6 @@ with dl_c: mime="application/json", ) -back_to_home_link(key="_back_to_home_link_bottom") - st.divider() st.caption("Runs locally. Your data never leaves this computer. | DataTools v3.0") diff --git a/src/i18n/packs/en.json b/src/i18n/packs/en.json index 3548628..c0a15e8 100644 --- a/src/i18n/packs/en.json +++ b/src/i18n/packs/en.json @@ -106,39 +106,57 @@ "tools": { "01_deduplicator": { "name": "Find Duplicates", - "description": "Fuzzy matching, normalization, survivor selection, and interactive review." + "description": "Fuzzy matching, normalization, survivor selection, and interactive review.", + "page_title": "🔍 Find Duplicates", + "page_caption": "Find and remove duplicate rows in CSV, delimited text, and Excel files." }, "02_text_cleaner": { "name": "Clean Text", - "description": "Whitespace trim, multi-space collapse, Unicode normalization, BOM and line-ending handling." + "description": "Whitespace trim, multi-space collapse, Unicode normalization, BOM and line-ending handling.", + "page_title": "✂️ Clean Text", + "page_caption": "Trim whitespace, fold smart quotes, strip invisible characters, and normalize line endings. Runs locally — your data never leaves this computer." }, "03_format_standardizer": { "name": "Standardize Formats", - "description": "Standardize dates, currencies, names, phone numbers, and addresses." + "description": "Standardize dates, currencies, names, phone numbers, and addresses.", + "page_title": "📐 Standardize Formats", + "page_caption": "Canonicalize dates, phone numbers, currency, names, addresses, and booleans on a per-column basis. Runs locally — your data never leaves this computer." }, "04_missing_handler": { "name": "Fix Missing Values", - "description": "Detect disguised nulls, missingness analysis, and imputation strategies." + "description": "Detect disguised nulls, missingness analysis, and imputation strategies.", + "page_title": "🕳️ Fix Missing Values", + "page_caption": "Detect disguised nulls, profile missingness, and apply imputation or drop strategies. Runs locally — your data never leaves this computer." }, "05_column_mapper": { "name": "Map Columns", - "description": "Rename columns, enforce a target schema, and coerce types." + "description": "Rename columns, enforce a target schema, and coerce types.", + "page_title": "🗂️ Map Columns", + "page_caption": "Rename columns, enforce a target schema, and coerce types. Runs locally — your data never leaves this computer." }, "06_outlier_detector": { "name": "Find Unusual Values", - "description": "Z-score, IQR, and MAD detection with domain-rule violations and winsorization." + "description": "Z-score, IQR, and MAD detection with domain-rule violations and winsorization.", + "page_title": "📊 Find Unusual Values", + "page_caption": "Detect and handle outliers in numeric columns." }, "07_multi_file_merger": { "name": "Combine Files", - "description": "Combine multiple CSV/Excel files with schema alignment." + "description": "Combine multiple CSV/Excel files with schema alignment.", + "page_title": "📎 Combine Files", + "page_caption": "Combine multiple CSV and Excel files into one dataset." }, "08_validator_reporter": { "name": "Quality Check", - "description": "Validate against rules and generate PDF/Excel quality reports." + "description": "Validate against rules and generate PDF/Excel quality reports.", + "page_title": "✅ Quality Check", + "page_caption": "Validate data against rules and generate quality reports." }, "09_pipeline_runner": { "name": "Automated Workflows", - "description": "Chain tools in recommended order and pass output between steps." + "description": "Chain tools in recommended order and pass output between steps.", + "page_title": "⚙️ Automated Workflows", + "page_caption": "Chain DataTools cleaning steps into one repeatable workflow. The pipeline recommends an order; you stay in control." } }, "nav": { diff --git a/src/i18n/packs/es.json b/src/i18n/packs/es.json index 3e67db8..1b41a7b 100644 --- a/src/i18n/packs/es.json +++ b/src/i18n/packs/es.json @@ -106,39 +106,57 @@ "tools": { "01_deduplicator": { "name": "Buscar duplicados", - "description": "Coincidencia difusa, normalización, selección de superviviente y revisión interactiva." + "description": "Coincidencia difusa, normalización, selección de superviviente y revisión interactiva.", + "page_title": "🔍 Buscar duplicados", + "page_caption": "Encuentra y elimina filas duplicadas en archivos CSV, texto delimitado y Excel." }, "02_text_cleaner": { "name": "Limpiar texto", - "description": "Recorte de espacios, colapso de espacios múltiples, normalización Unicode, manejo de BOM y de finales de línea." + "description": "Recorte de espacios, colapso de espacios múltiples, normalización Unicode, manejo de BOM y de finales de línea.", + "page_title": "✂️ Limpiar texto", + "page_caption": "Recorta espacios, normaliza comillas tipográficas, elimina caracteres invisibles y unifica saltos de línea. Se ejecuta localmente — tus datos nunca salen de este equipo." }, "03_format_standardizer": { "name": "Estandarizar formatos", - "description": "Estandariza fechas, monedas, nombres, números de teléfono y direcciones." + "description": "Estandariza fechas, monedas, nombres, números de teléfono y direcciones.", + "page_title": "📐 Estandarizar formatos", + "page_caption": "Canoniza fechas, números de teléfono, monedas, nombres, direcciones y booleanos columna por columna. Se ejecuta localmente — tus datos nunca salen de este equipo." }, "04_missing_handler": { "name": "Corregir valores faltantes", - "description": "Detecta nulos disfrazados, analiza la ausencia de datos y aplica estrategias de imputación." + "description": "Detecta nulos disfrazados, analiza la ausencia de datos y aplica estrategias de imputación.", + "page_title": "🕳️ Corregir valores faltantes", + "page_caption": "Detecta nulos disfrazados, perfila la ausencia de datos y aplica imputación o estrategias de descarte. Se ejecuta localmente — tus datos nunca salen de este equipo." }, "05_column_mapper": { "name": "Mapear columnas", - "description": "Renombra columnas, aplica un esquema objetivo y fuerza tipos de datos." + "description": "Renombra columnas, aplica un esquema objetivo y fuerza tipos de datos.", + "page_title": "🗂️ Mapear columnas", + "page_caption": "Renombra columnas, aplica un esquema objetivo y fuerza tipos. Se ejecuta localmente — tus datos nunca salen de este equipo." }, "06_outlier_detector": { "name": "Detectar valores atípicos", - "description": "Detección por Z-score, IQR y MAD con reglas de dominio y winsorización." + "description": "Detección por Z-score, IQR y MAD con reglas de dominio y winsorización.", + "page_title": "📊 Detectar valores atípicos", + "page_caption": "Detecta y trata valores atípicos en columnas numéricas." }, "07_multi_file_merger": { "name": "Combinar archivos", - "description": "Combina varios archivos CSV/Excel alineando sus esquemas." + "description": "Combina varios archivos CSV/Excel alineando sus esquemas.", + "page_title": "📎 Combinar archivos", + "page_caption": "Combina varios archivos CSV y Excel en un único conjunto de datos." }, "08_validator_reporter": { "name": "Verificación de calidad", - "description": "Valida contra reglas y genera informes de calidad en PDF/Excel." + "description": "Valida contra reglas y genera informes de calidad en PDF/Excel.", + "page_title": "✅ Verificación de calidad", + "page_caption": "Valida datos contra reglas y genera informes de calidad." }, "09_pipeline_runner": { "name": "Flujos automatizados", - "description": "Encadena herramientas en el orden recomendado y pasa la salida entre pasos." + "description": "Encadena herramientas en el orden recomendado y pasa la salida entre pasos.", + "page_title": "⚙️ Flujos automatizados", + "page_caption": "Encadena pasos de limpieza de DataTools en un flujo repetible. La canalización recomienda un orden; tú mantienes el control." } }, "nav": { diff --git a/tests/gui/test_smoke.py b/tests/gui/test_smoke.py index e2da6b2..160ee24 100644 --- a/tests/gui/test_smoke.py +++ b/tests/gui/test_smoke.py @@ -52,15 +52,15 @@ PAGE_SLUGS = [ # When a page gains real Spanish translation, flip its 'es' entry to # the localized substring — the test surface stays the same. EXPECTED_SUBSTRINGS: dict[str, dict[str, str]] = { - "1_Deduplicator": {"en": "Find Duplicates", "es": "Find Duplicates"}, - "2_Text_Cleaner": {"en": "Clean Text", "es": "Clean Text"}, - "3_Format_Standardizer": {"en": "Standardize", "es": "Standardize"}, - "4_Missing_Values": {"en": "Fix Missing", "es": "Fix Missing"}, - "5_Column_Mapper": {"en": "Map Columns", "es": "Map Columns"}, - "6_Outlier_Detector": {"en": "Unusual", "es": "Unusual"}, - "7_Multi_File_Merger": {"en": "Combine Files", "es": "Combine Files"}, - "8_Validator_Reporter": {"en": "Quality Check", "es": "Quality Check"}, - "9_Pipeline_Runner": {"en": "Automated", "es": "Automated"}, + "1_Deduplicator": {"en": "Find Duplicates", "es": "Buscar duplicados"}, + "2_Text_Cleaner": {"en": "Clean Text", "es": "Limpiar texto"}, + "3_Format_Standardizer": {"en": "Standardize", "es": "Estandarizar"}, + "4_Missing_Values": {"en": "Fix Missing", "es": "Corregir valores"}, + "5_Column_Mapper": {"en": "Map Columns", "es": "Mapear columnas"}, + "6_Outlier_Detector": {"en": "Unusual", "es": "atípicos"}, + "7_Multi_File_Merger": {"en": "Combine Files", "es": "Combinar archivos"}, + "8_Validator_Reporter": {"en": "Quality Check", "es": "Verificación de calidad"}, + "9_Pipeline_Runner": {"en": "Automated", "es": "Flujos automatizados"}, "99_Close": {"en": "Shutting down", "es": "Cerrando"}, }