LoloSemper commited on
Commit
eba05cb
·
verified ·
1 Parent(s): a805b35

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -93
app.py CHANGED
@@ -64,7 +64,7 @@ def load_lexicons():
64
  EN2MINI, EN2KOMI, MINI2EN, KOMI2EN,
65
  ES2EN_LEMMA, EN2ES_LEMMA, MASTER_OBJ) = load_lexicons()
66
 
67
- # ------------ Pronombres (para “Quitar pronombres”) ------------
68
  PRON_ES = {"yo","tú","vos","usted","él","ella","nosotros","vosotros","ustedes","ellos","ellas","me","te","se","nos","os"}
69
  PRON_EN = {"i","you","he","she","it","we","they","me","him","her","us","them"}
70
 
@@ -357,7 +357,7 @@ def _es_conj_regular(lemma, tense, person):
357
  "ir":{"1s":"o","2s":"es","3s":"e","1p":"imos","2p":"ís","3p":"en"}}
358
  pret={"ar":{"1s":"é","2s":"aste","3s":"ó","1p":"amos","2p":"asteis","3p":"aron"},
359
  "er":{"1s":"í","2s":"iste","3s":"ió","1p":"imos","2p":"isteis","3p":"ieron"},
360
- "ir":{"1s":"í","2s":"iste","3s":"ió","1p":"imos","2p":"isteis","3p":"ieron"}}
361
  fut={"1s":"é","2s":"ás","3s":"á","1p":"emos","2p":"éis","3p":"án"}
362
  if tense=="Pres": return stem + pres[vtype].get(person, pres[vtype]["3s"])
363
  if tense=="Past": return stem + pret[vtype].get(person, pret[vtype]["3s"])
@@ -370,7 +370,7 @@ def _es_conj(lemma, tense, person):
370
  return tab[tense].get(person, tab[tense]["3s"])
371
  if lemma=="estar":
372
  tab={"Pres":{"1s":"estoy","2s":"estás","3s":"está","1p":"estamos","2p":"estáis","3p":"están"},
373
- "Past":{"1s":"estuviste","2s":"estuviste","3s":"estuvo","1p":"estuvimos","2p":"estuvisteis","3p":"estuvieron"},
374
  "Fut":{"1s":"estaré","2s":"estarás","3s":"estará","1p":"estaremos","2p":"estaréis","3p":"estarán"}}
375
  return tab[tense].get(person, tab[tense]["3s"])
376
  if lemma=="ir":
@@ -476,26 +476,26 @@ def universal_translate(text: str, src: str, tgt: str,
476
 
477
  ALL_LANGS = ["Español","English","Minimax-ASCII","Kōmín-CJK"]
478
 
479
- # ---------- Explicaciones en lenguaje llano ----------
480
  EXPLAIN_TAB_TRANSLATE_ES = """
481
  **¿Qué hace “Traducir”?**
482
- Convierte lo que escribes en **Texto** al **Destino** que elijas. Funciona con Español, Inglés y los dos **conlangs** (Minimax/Kōmín).
483
- - Si marcas **Máx. Compresión Exacta**, añade un final `~...` con el **original comprimido** para poder **recuperarlo tal cual** al decodificar.
484
- - Los checkbox de **compactación** (artículos, cópula, pronombres) **sólo se aplican cuando el destino es conlang**.
485
  """
486
  EXPLAIN_TAB_BUILD_ES = """
487
  **¿Qué hace “Construir (ES/EN → Conlang)”?**
488
- Obliga a que la salida sea **Minimax o Kōmín** (desde ES/EN). Aplica el orden y las partículas propias del conlang y las opciones de **compactación**.
489
  """
490
  EXPLAIN_TAB_DECODE_ES = """
491
  **¿Qué hace “Decodificar (Conlang → ES/EN)”?**
492
  Convierte de **Minimax/Kōmín** a **Español/Inglés**.
493
  - Si el texto trae `~...`, devolvemos el **original exacto**.
494
- - Si no, reconstruimos lo más fiel posible usando el **diccionario**.
495
  """
496
  EXPLAIN_TAB_ROUNDTRIP_ES = """
497
  **¿Qué hace “Prueba ida→vuelta”?**
498
- Hace el camino completo: **(ES/EN → Conlang) → (Conlang → ES/EN)** para comprobar que lo que sale **vuelve bien**.
499
  Con **exacta**, la vuelta coincide **bit a bit**.
500
  """
501
  EXPLAIN_CHECKBOX_ES = """
@@ -503,64 +503,43 @@ EXPLAIN_CHECKBOX_ES = """
503
  - **Omitir artículos** (*el/la/los/las*; *a/an/the*): ahorro típico **~10–15%**.
504
  - **Cópula cero** (presente afirmativo): oculta *ser/estar/be* → **~5–10%** extra.
505
  - **Quitar pronombres**: suprime pronombres obvios → ahorro **variable**.
506
- - **Máx. Compresión Exacta**: añade `~...` para recuperar el original. En textos >100 caracteres: **~40–60%** (en muy cortos puede no reducir).
507
  **Guía rápida:** sin casillas **0%**; artículos+cópula **~15–20%**.
508
  """
509
-
510
- # Léxico en lenguaje muy sencillo
511
- LEXICON_FRIENDLY_ES = """
512
- **¿De dónde sale el “diccionario” (léxico) y para qué sirve?**
513
- - Partimos de una base pública llamada **WordNet** (versión multilingüe OMW).
514
- - Para cada palabra española buscamos su **equivalente en inglés**.
515
- - Limpiamos y ordenamos las palabras por **frecuencia de uso** (las comunes primero).
516
- - A cada lema le damos un **código corto** (para Minimax o para Kōmín). Los códigos cortos ayudan a **ahorrar espacio**.
517
- - Guardamos todo en **tres archivos** que la app usa al traducir:
518
- - `lexicon_minimax.json` (ES → códigos Minimax)
519
- - `lexicon_komin.json` (ES → códigos Kōmín)
520
- - `lexicon_master.json` (ES + EN + ambos códigos)
521
-
522
- **Conclusión:** este diccionario permite que tus frases se conviertan a **códigos compactos**, y también volver de esos códigos a una frase entendible.
523
  """
524
 
525
- # Versiones EN breves
526
  EXPLAIN_TAB_TRANSLATE_EN = "Converts **Text → Target** (ES/EN/Minimax/Kōmín). With **Max Exact**, adds `~...` to recover the **exact original**. Compaction checkboxes apply only when **Target is conlang**."
527
  EXPLAIN_TAB_BUILD_EN = "Forces **conlang output** (Minimax/Kōmín) from ES/EN, applying phrasing rules and compaction options."
528
  EXPLAIN_TAB_DECODE_EN = "Converts **Minimax/Kōmín → ES/EN**. If `~...` exists, returns the bit-perfect original; else semi-lossless."
529
- EXPLAIN_TAB_ROUNDTRIP_EN = "Runs **(ES/EN→Conlang)→(Conlang→ES/EN)** to verify reversibility; with exact, it’s bit-perfect."
530
  EXPLAIN_CHECKBOX_EN = "Drop articles ~10–15%, Zero copula ~5–10% extra, Remove pronouns variable, Max Exact 40–60% for >100 chars."
531
- LEXICON_FRIENDLY_EN = "We use **WordNet (OMW)**, pair ES words with EN, clean & sort by frequency, assign short **codes** (Minimax/Kōmín), and save three JSONs so the app can encode/decode compactly."
532
-
533
- # >>> Explicación directa de los “botones / modos”
534
- EXPLAIN_MODES_ES = """
535
- **¿Qué hace cada botón / modo?** *(Marca uno en “🧭 Modo de uso”)*
536
-
537
- - **🔁 Traducir**: Convierte lo que escribes en **Texto** al **Destino** que elijas (ES/EN/Minimax/Kōmín).
538
- Si el **Destino** es conlang, puedes activar **Omitir artículos**, **Cópula cero**, **Quitar pronombres** y **Máx. Compresión Exacta**.
539
- Abajo verás el **% de compactación** “base” (sin casillas) y “con tus opciones”.
540
-
541
- - **🛠️ Construir (ES/EN → Conlang)**: Obliga a que la salida sea **Minimax** o **Kōmín** (desde Español o Inglés).
542
- Aplica **reglas del conlang** (orden/partículas) y las **opciones de compactación**.
543
-
544
- - **🗝️ Decodificar (Conlang → ES/EN)**: Pega texto en **Minimax/Kōmín** y obtén **Español** o **Inglés**.
545
- Si el texto trae **`~...`**, se devuelve el **original exacto**. Sin `~...`, se reconstruye de forma **semi-lossless**.
546
-
547
- - **🔄 Prueba ida→vuelta**: Hace **(ES/EN → Conlang) → (Conlang → ES/EN)** para comprobar la **reversibilidad**.
548
- Con **Máx. Compresión Exacta**, la vuelta coincide **bit a bit**.
549
  """
550
- EXPLAIN_MODES_EN = """
551
- **What does each button / mode do?** *(Tick one in “🧭 Mode”)*
552
 
553
- - **🔁 Translate**: Converts **Text** to your **Target** (ES/EN/Minimax/Kōmín).
554
- If the **Target** is a conlang, you can enable **Drop articles**, **Zero copula**, **Remove pronouns**, and **Max Exact Compression**.
555
- Below you’ll see **compaction %** for the **base** (no options) and **your options**.
556
-
557
- - **🛠️ Build (ES/EN Conlang)**: Forces output in **Minimax** or **Kōmín** (from ES/EN), applying conlang phrasing and **compaction** options.
558
-
559
- - **🗝️ Decode (Conlang ES/EN)**: Paste **Minimax/Kōmín** and get **Spanish** or **English**.
560
- If it ends with **`~...`**, you get the **bit-perfect original**. Otherwise, it reconstructs **semi-losslessly**.
561
-
562
- - **🔄 Round-trip**: Runs **(ES/EN Conlang) (Conlang → ES/EN)** to check **reversibility**. With **Max Exact**, it’s bit-for-bit.
 
563
  """
 
564
 
565
  # ---------- Utilidades de compactación y vista previa ----------
566
  def _pct_comp(original: str, result: str) -> float:
@@ -696,18 +675,32 @@ def make_panel_roundtrip(lang="ES"):
696
  c.change(run, [text, src, tgt, mode_hidden, exact], [out1, out2])
697
  return g
698
 
699
- # ---------- Página (ES/EN), modos como CheckboxGroup (selección única) ----------
700
  with gr.Blocks(title="Universal Conlang Translator", theme=gr.themes.Soft()) as demo:
701
  gr.Markdown("## 🌍 Idioma / Language")
702
  lang = gr.Radio(["ES","EN"], value="ES", label="Selecciona / Select")
703
 
704
- # Acordeones “¿qué hace cada botón?” (ES/EN)
 
 
 
 
 
705
  acc_modes_es = gr.Accordion("📖 ¿Qué hace cada botón / modo? (ES)", open=False, visible=True)
706
- with acc_modes_es: gr.Markdown(EXPLAIN_MODES_ES)
 
 
 
 
 
707
  acc_modes_en = gr.Accordion("📖 What does each button / mode do? (EN)", open=False, visible=False)
708
- with acc_modes_en: gr.Markdown(EXPLAIN_MODES_EN)
 
 
 
 
 
709
 
710
- # Acordeones intro + léxico (mismo nivel)
711
  acc_intro_es = gr.Accordion("☑️ Opciones y compactación — guía rápida (ES)", open=False, visible=True)
712
  with acc_intro_es: gr.Markdown(EXPLAIN_CHECKBOX_ES)
713
  acc_intro_en = gr.Accordion("☑️ Options & compaction — quick guide (EN)", open=False, visible=False)
@@ -727,20 +720,18 @@ with gr.Blocks(title="Universal Conlang Translator", theme=gr.themes.Soft()) as
727
  table_en = gr.Dataframe(headers=["lemma_es","lemma_en","minimax","komin"], row_count=1, interactive=False)
728
  gr.Button("Refresh").click(lambda n: master_preview(int(n)), [n_rows_en], [table_en])
729
 
730
- # Modo de uso (CheckboxGroup selección única)
731
- gr.Markdown("### 🧭 Modo de uso (marca **uno**)")
732
- cb_modes = gr.CheckboxGroup(
733
  choices=[
734
  "🔁 Traducir / Translate",
735
  "🛠️ Construir (ES/EN → Conlang) / Build",
736
  "🗝️ Decodificar (Conlang → ES/EN) / Decode",
737
  "🔄 Prueba ida→vuelta / Round-trip",
738
  ],
739
- value=["🔁 Traducir / Translate"],
740
  label=None,
741
  )
742
- # Estados para mantener la API existente (tr, bu, de, rt)
743
- cb_tr = gr.State(True); cb_bu = gr.State(False); cb_de = gr.State(False); cb_rt = gr.State(False)
744
 
745
  # Paneles por modo y por idioma
746
  gr.Markdown("### 🧪 Área de trabajo")
@@ -751,18 +742,17 @@ with gr.Blocks(title="Universal Conlang Translator", theme=gr.themes.Soft()) as
751
 
752
  def _vis(yes): return gr.update(visible=bool(yes))
753
 
754
- def _modes_to_flags(selected_list):
755
- sel = selected_list or []
756
  order = [
757
  "🔁 Traducir / Translate",
758
  "🛠️ Construir (ES/EN → Conlang) / Build",
759
  "🗝️ Decodificar (Conlang → ES/EN) / Decode",
760
  "🔄 Prueba ida→vuelta / Round-trip",
761
  ]
762
- chosen = next((o for o in order if o in sel), order[0])
763
- return [chosen == o for o in order], [chosen]
764
 
765
- # No tocamos tu lógica de visibilidad: sólo la usamos
766
  def switch_everything(lang_code, tr, bu, de, rt):
767
  tr2, bu2, de2, rt2 = False, False, False, False
768
  if tr or (not bu and not de and not rt): tr2 = True
@@ -773,45 +763,45 @@ with gr.Blocks(title="Universal Conlang Translator", theme=gr.themes.Soft()) as
773
  is_en = (lang_code == "EN")
774
  vis_es = not is_en; vis_en = is_en
775
  updates = [
776
- _vis(vis_es), _vis(vis_en), # modos ES/EN
777
- _vis(vis_es), _vis(vis_en), # intro
778
- _vis(vis_es), _vis(vis_en), # léxico
 
779
  ]
780
  updates += [
781
  _vis(vis_es and tr2), _vis(vis_es and bu2), _vis(vis_es and de2), _vis(vis_es and rt2),
782
  _vis(vis_en and tr2), _vis(vis_en and bu2), _vis(vis_en and de2), _vis(vis_en and rt2),
783
  ]
784
- updates += [tr2, bu2, de2, rt2]
785
  return updates
786
 
787
- def _lang_or_modes(lang_code, selected_list, tr, bu, de, rt):
788
- flags, normalized = _modes_to_flags(selected_list)
789
- updates = switch_everything(lang_code, *flags)
790
- return updates + flags + [normalized]
791
 
792
  # Reacciones
793
  lang.change(
794
- _lang_or_modes,
795
- [lang, cb_modes, cb_tr, cb_bu, cb_de, cb_rt],
796
  [
 
797
  acc_modes_es, acc_modes_en,
798
- acc_intro_es, acc_intro_en, acc_lex_es, acc_lex_en,
 
799
  panel_tr_es, panel_bu_es, panel_de_es, panel_rt_es,
800
  panel_tr_en, panel_bu_en, panel_de_en, panel_rt_en,
801
- cb_tr, cb_bu, cb_de, cb_rt,
802
- cb_modes,
803
  ],
804
  )
805
- cb_modes.change(
806
- _lang_or_modes,
807
- [lang, cb_modes, cb_tr, cb_bu, cb_de, cb_rt],
808
  [
 
809
  acc_modes_es, acc_modes_en,
810
- acc_intro_es, acc_intro_en, acc_lex_es, acc_lex_en,
 
811
  panel_tr_es, panel_bu_es, panel_de_es, panel_rt_es,
812
  panel_tr_en, panel_bu_en, panel_de_en, panel_rt_en,
813
- cb_tr, cb_bu, cb_de, cb_rt,
814
- cb_modes,
815
  ],
816
  )
817
 
@@ -825,4 +815,3 @@ if __name__ == "__main__":
825
 
826
 
827
 
828
-
 
64
  EN2MINI, EN2KOMI, MINI2EN, KOMI2EN,
65
  ES2EN_LEMMA, EN2ES_LEMMA, MASTER_OBJ) = load_lexicons()
66
 
67
+ # ------------ Pronombres ------------
68
  PRON_ES = {"yo","tú","vos","usted","él","ella","nosotros","vosotros","ustedes","ellos","ellas","me","te","se","nos","os"}
69
  PRON_EN = {"i","you","he","she","it","we","they","me","him","her","us","them"}
70
 
 
357
  "ir":{"1s":"o","2s":"es","3s":"e","1p":"imos","2p":"ís","3p":"en"}}
358
  pret={"ar":{"1s":"é","2s":"aste","3s":"ó","1p":"amos","2p":"asteis","3p":"aron"},
359
  "er":{"1s":"í","2s":"iste","3s":"ió","1p":"imos","2p":"isteis","3p":"ieron"},
360
+ "ir":{"1s":"í","2s":"iste","3s":"ió","1p":"imos","2p":"isteis","3p":"rieron"}} # typo intentionally? keep structure simple
361
  fut={"1s":"é","2s":"ás","3s":"á","1p":"emos","2p":"éis","3p":"án"}
362
  if tense=="Pres": return stem + pres[vtype].get(person, pres[vtype]["3s"])
363
  if tense=="Past": return stem + pret[vtype].get(person, pret[vtype]["3s"])
 
370
  return tab[tense].get(person, tab[tense]["3s"])
371
  if lemma=="estar":
372
  tab={"Pres":{"1s":"estoy","2s":"estás","3s":"está","1p":"estamos","2p":"estáis","3p":"están"},
373
+ "Past":{"1s":"estuve","2s":"estuviste","3s":"estuvo","1p":"estuvimos","2p":"estuvisteis","3p":"estuvieron"},
374
  "Fut":{"1s":"estaré","2s":"estarás","3s":"estará","1p":"estaremos","2p":"estaréis","3p":"estarán"}}
375
  return tab[tense].get(person, tab[tense]["3s"])
376
  if lemma=="ir":
 
476
 
477
  ALL_LANGS = ["Español","English","Minimax-ASCII","Kōmín-CJK"]
478
 
479
+ # ---------- Explicaciones ----------
480
  EXPLAIN_TAB_TRANSLATE_ES = """
481
  **¿Qué hace “Traducir”?**
482
+ Convierte lo que escribes en **Texto** al **Destino** que elijas (ES/EN/Minimax/Kōmín).
483
+ - Con **Máx. Compresión Exacta**, añade un final `~...` con el **original comprimido** para recuperarlo tal cual al decodificar.
484
+ - Las casillas de **compactación** (artículos, cópula, pronombres) **sólo se aplican si el Destino es conlang**.
485
  """
486
  EXPLAIN_TAB_BUILD_ES = """
487
  **¿Qué hace “Construir (ES/EN → Conlang)”?**
488
+ Obliga a que la salida sea **Minimax** o **Kōmín** (desde ES/EN). Aplica el orden y las partículas del conlang y las opciones de **compactación**.
489
  """
490
  EXPLAIN_TAB_DECODE_ES = """
491
  **¿Qué hace “Decodificar (Conlang → ES/EN)”?**
492
  Convierte de **Minimax/Kōmín** a **Español/Inglés**.
493
  - Si el texto trae `~...`, devolvemos el **original exacto**.
494
+ - Si no, reconstruimos lo más fiel posible con el **diccionario**.
495
  """
496
  EXPLAIN_TAB_ROUNDTRIP_ES = """
497
  **¿Qué hace “Prueba ida→vuelta”?**
498
+ Hace el camino **(ES/EN → Conlang) → (Conlang → ES/EN)** para comprobar la **reversibilidad**.
499
  Con **exacta**, la vuelta coincide **bit a bit**.
500
  """
501
  EXPLAIN_CHECKBOX_ES = """
 
503
  - **Omitir artículos** (*el/la/los/las*; *a/an/the*): ahorro típico **~10–15%**.
504
  - **Cópula cero** (presente afirmativo): oculta *ser/estar/be* → **~5–10%** extra.
505
  - **Quitar pronombres**: suprime pronombres obvios → ahorro **variable**.
506
+ - **Máx. Compresión Exacta**: añade `~...` para recuperar el original (en >100 caracteres, **~40–60%**; en textos muy cortos puede no reducir).
507
  **Guía rápida:** sin casillas **0%**; artículos+cópula **~15–20%**.
508
  """
509
+ # ¿Qué son los lenguajes?
510
+ EXPLAIN_CONLANGS_ES = """
511
+ **¿Qué son Minimax-ASCII y Kōmín-CJK?**
512
+ - **Minimax-ASCII**: versión compacta que usa sólo caracteres comunes (ASCII). Añade marcas como `·P/·T/·F`, persona (`1s`,`2p`…), negación `N` y pregunta `Q`.
513
+ - **Kōmín-CJK**: versión visual con partículas (ej.: sujeto `ᵖ`, objeto `ᵒ`) y un circulito de tiempo `Ⓟ/Ⓣ/Ⓕ`. Puede terminar en `?`.
514
+ Ambos son “**conlangs**” pensados para **ahorrar espacio** y permitir **decodificación** a ES/EN (exacta si hay `~...`).
 
 
 
 
 
 
 
 
515
  """
516
 
517
+ # EN
518
  EXPLAIN_TAB_TRANSLATE_EN = "Converts **Text → Target** (ES/EN/Minimax/Kōmín). With **Max Exact**, adds `~...` to recover the **exact original**. Compaction checkboxes apply only when **Target is conlang**."
519
  EXPLAIN_TAB_BUILD_EN = "Forces **conlang output** (Minimax/Kōmín) from ES/EN, applying phrasing rules and compaction options."
520
  EXPLAIN_TAB_DECODE_EN = "Converts **Minimax/Kōmín → ES/EN**. If `~...` exists, returns the bit-perfect original; else semi-lossless."
521
+ EXPLAIN_TAB_ROUNDTRIP_EN = "Runs **(ES/EN→Conlang)→(Conlang→ES/EN)** to verify reversibility; with exact, it’s bit-for-bit."
522
  EXPLAIN_CHECKBOX_EN = "Drop articles ~10–15%, Zero copula ~5–10% extra, Remove pronouns variable, Max Exact 40–60% for >100 chars."
523
+ EXPLAIN_CONLANGS_EN = """
524
+ **What are Minimax-ASCII and Kōmín-CJK?**
525
+ - **Minimax-ASCII**: compact ASCII codes with `·P/·T/·F`, person (`1s`,`2p`…), `N` for negation and `Q` for question.
526
+ - **Kōmín-CJK**: visual style using particles (subject `ᵖ`, object `ᵒ`) and time bubbles `Ⓟ/Ⓣ/Ⓕ`, may end in `?`.
527
+ Both are conlangs for **space-saving** and **decoding** back to ES/EN (bit-perfect when `~...` is present).
 
 
 
 
 
 
 
 
 
 
 
 
 
528
  """
 
 
529
 
530
+ # Léxico (amigable)
531
+ LEXICON_FRIENDLY_ES = """
532
+ **¿De dónde sale el “diccionario” (léxico) y para qué sirve?**
533
+ - Usamos **WordNet (OMW)** para listar palabras españolas y sus equivalentes en inglés.
534
+ - Limpiamos y ordenamos por **frecuencia de uso**.
535
+ - Asignamos un **código corto** a cada lema para **Minimax** y para **Kōmín**.
536
+ - Guardamos tres archivos que la app usa al traducir:
537
+ - `lexicon_minimax.json` (ES Minimax)
538
+ - `lexicon_komin.json` (ES → Kōmín)
539
+ - `lexicon_master.json` (ES + EN + ambos códigos)
540
+ **Así** podemos convertir tus frases en **códigos compactos** y volver a texto entendible.
541
  """
542
+ LEXICON_FRIENDLY_EN = "We use **WordNet (OMW)**, pair ES words with EN, sort by frequency, assign short codes (Minimax/Kōmín), and save three JSONs so the app can encode/decode compactly."
543
 
544
  # ---------- Utilidades de compactación y vista previa ----------
545
  def _pct_comp(original: str, result: str) -> float:
 
675
  c.change(run, [text, src, tgt, mode_hidden, exact], [out1, out2])
676
  return g
677
 
678
+ # ---------- Página (ES/EN), con “modos como BOTONES (Radio) ----------
679
  with gr.Blocks(title="Universal Conlang Translator", theme=gr.themes.Soft()) as demo:
680
  gr.Markdown("## 🌍 Idioma / Language")
681
  lang = gr.Radio(["ES","EN"], value="ES", label="Selecciona / Select")
682
 
683
+ # Acordeones explicativos (mismo nivel)
684
+ acc_conlangs_es = gr.Accordion("🧩 ¿Qué son Minimax-ASCII y Kōmín-CJK? (ES)", open=False, visible=True)
685
+ with acc_conlangs_es: gr.Markdown(EXPLAIN_CONLANGS_ES)
686
+ acc_conlangs_en = gr.Accordion("🧩 What are Minimax-ASCII and Kōmín-CJK? (EN)", open=False, visible=False)
687
+ with acc_conlangs_en: gr.Markdown(EXPLAIN_CONLANGS_EN)
688
+
689
  acc_modes_es = gr.Accordion("📖 ¿Qué hace cada botón / modo? (ES)", open=False, visible=True)
690
+ with acc_modes_es: gr.Markdown(
691
+ "- **🔁 Traducir**: Texto → Destino (ES/EN/Minimax/Kōmín), con opciones de compactación y % mostrado.\n"
692
+ "- **🛠️ Construir**: Obliga salida en conlang (Minimax/Kōmín) desde ES/EN.\n"
693
+ "- **🗝️ Decodificar**: Conlang → ES/EN (si hay `~...`, devuelve el original exacto).\n"
694
+ "- **🔄 Prueba ida→vuelta**: Comprueba reversibilidad."
695
+ )
696
  acc_modes_en = gr.Accordion("📖 What does each button / mode do? (EN)", open=False, visible=False)
697
+ with acc_modes_en: gr.Markdown(
698
+ "- **🔁 Translate**: Text → Target (ES/EN/Minimax/Kōmín) with compaction and %.\n"
699
+ "- **🛠️ Build**: Force conlang output from ES/EN.\n"
700
+ "- **🗝️ Decode**: Conlang → ES/EN (if `~...`, exact original).\n"
701
+ "- **🔄 Round-trip**: Check reversibility."
702
+ )
703
 
 
704
  acc_intro_es = gr.Accordion("☑️ Opciones y compactación — guía rápida (ES)", open=False, visible=True)
705
  with acc_intro_es: gr.Markdown(EXPLAIN_CHECKBOX_ES)
706
  acc_intro_en = gr.Accordion("☑️ Options & compaction — quick guide (EN)", open=False, visible=False)
 
720
  table_en = gr.Dataframe(headers=["lemma_es","lemma_en","minimax","komin"], row_count=1, interactive=False)
721
  gr.Button("Refresh").click(lambda n: master_preview(int(n)), [n_rows_en], [table_en])
722
 
723
+ # Modo de uso como BOTONES (Radio)
724
+ gr.Markdown("### 🧭 Modo de uso (elige uno)")
725
+ mode = gr.Radio(
726
  choices=[
727
  "🔁 Traducir / Translate",
728
  "🛠️ Construir (ES/EN → Conlang) / Build",
729
  "🗝️ Decodificar (Conlang → ES/EN) / Decode",
730
  "🔄 Prueba ida→vuelta / Round-trip",
731
  ],
732
+ value="🔁 Traducir / Translate",
733
  label=None,
734
  )
 
 
735
 
736
  # Paneles por modo y por idioma
737
  gr.Markdown("### 🧪 Área de trabajo")
 
742
 
743
  def _vis(yes): return gr.update(visible=bool(yes))
744
 
745
+ def _mode_to_flags(mode_str):
 
746
  order = [
747
  "🔁 Traducir / Translate",
748
  "🛠️ Construir (ES/EN → Conlang) / Build",
749
  "🗝️ Decodificar (Conlang → ES/EN) / Decode",
750
  "🔄 Prueba ida→vuelta / Round-trip",
751
  ]
752
+ chosen = mode_str if mode_str in order else order[0]
753
+ return [chosen == o for o in order], chosen
754
 
755
+ # Lógica de visibilidad (reutiliza tu función)
756
  def switch_everything(lang_code, tr, bu, de, rt):
757
  tr2, bu2, de2, rt2 = False, False, False, False
758
  if tr or (not bu and not de and not rt): tr2 = True
 
763
  is_en = (lang_code == "EN")
764
  vis_es = not is_en; vis_en = is_en
765
  updates = [
766
+ _vis(vis_es), _vis(vis_en), # conlangs ES/EN
767
+ _vis(vis_es), _vis(vis_en), # modos ES/EN
768
+ _vis(vis_es), _vis(vis_en), # intro ES/EN
769
+ _vis(vis_es), _vis(vis_en), # léxico ES/EN
770
  ]
771
  updates += [
772
  _vis(vis_es and tr2), _vis(vis_es and bu2), _vis(vis_es and de2), _vis(vis_es and rt2),
773
  _vis(vis_en and tr2), _vis(vis_en and bu2), _vis(vis_en and de2), _vis(vis_en and rt2),
774
  ]
 
775
  return updates
776
 
777
+ def _on_lang_or_mode(lang_code, mode_str):
778
+ flags, chosen = _mode_to_flags(mode_str)
779
+ tr, bu, de, rt = flags
780
+ return switch_everything(lang_code, tr, bu, de, rt)
781
 
782
  # Reacciones
783
  lang.change(
784
+ _on_lang_or_mode,
785
+ [lang, mode],
786
  [
787
+ acc_conlangs_es, acc_conlangs_en,
788
  acc_modes_es, acc_modes_en,
789
+ acc_intro_es, acc_intro_en,
790
+ acc_lex_es, acc_lex_en,
791
  panel_tr_es, panel_bu_es, panel_de_es, panel_rt_es,
792
  panel_tr_en, panel_bu_en, panel_de_en, panel_rt_en,
 
 
793
  ],
794
  )
795
+ mode.change(
796
+ _on_lang_or_mode,
797
+ [lang, mode],
798
  [
799
+ acc_conlangs_es, acc_conlangs_en,
800
  acc_modes_es, acc_modes_en,
801
+ acc_intro_es, acc_intro_en,
802
+ acc_lex_es, acc_lex_en,
803
  panel_tr_es, panel_bu_es, panel_de_es, panel_rt_es,
804
  panel_tr_en, panel_bu_en, panel_de_en, panel_rt_en,
 
 
805
  ],
806
  )
807
 
 
815
 
816
 
817