@danhachuel/thunderbolt 0.2.81 → 0.2.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -194,7 +194,7 @@ npx --yes @danhachuel/thunderbolt install
194
194
 
195
195
  O Thunderbolt inclui as dez opções de idioma usadas pelo fluxo MoneyPrinterTurbo: **Inglês (en) 🇺🇸**, **Chinês Simplificado (zh) 🇨🇳**, **Alemão (de) 🇩🇪**, **Vietnamita (vi) 🇻🇳**, **Turco (tr) 🇹🇷**, **Português (pt) 🇧🇷**, **Russo (ru) 🇷🇺**, **Espanhol (es) 🇪🇸**, **Indonésio (id) 🇮🇩** e **Italiano (it) 🇮🇹**. A criação de vídeos mantém ainda os rótulos históricos para não invalidar Blueprints e tarefas antigas.
196
196
 
197
- No topo da área principal, o menu nativo de idioma segue o padrão do MoneyPrinterTurbo e mostra cada opção como **nome do idioma + bandeira**. A escolha fica guardada como `ui_language` em `storage/state/settings.json` e, quando existe um caminho MoneyPrinterTurbo configurado, é sincronizada com `[ui].language` no `config.toml`. O toolbar do Streamlit não é manipulado: o indicador de execução, **Deploy** e o menu de três pontos permanecem nativos e clicáveis. Na Criação de Vídeos e em Roteiros, o selector **Script Language** mostra bandeira, nome e código, mas guarda o código curto; essa escolha é sincronizada com `[ui].video_language`.
197
+ No topo da área principal, o menu nativo de idioma segue o padrão do MoneyPrinterTurbo e mostra o rótulo **Language** acima do selector. Cada opção apresenta o nome e o código, acompanhados por uma imagem SVG local da bandeira; não depende de emojis nem de siglas da fonte do sistema. A escolha fica guardada como `ui_language` em `storage/state/settings.json` e, quando existe um caminho MoneyPrinterTurbo configurado, é sincronizada com `[ui].language` no `config.toml`. O dashboard inicial e as abas da barra lateral usam o mesmo idioma seleccionado. O toolbar do Streamlit não é manipulado: o indicador de execução, **Deploy** e o menu de três pontos permanecem nativos e clicáveis. Na Criação de Vídeos e em Roteiros, o selector **Script Language** mantém o catálogo de vídeo independente, mas guarda o código curto; essa escolha é sincronizada com `[ui].video_language`.
198
198
 
199
199
  ## 4. Diagnóstico antes de iniciar
200
200
 
package/README.md CHANGED
@@ -50,9 +50,9 @@ Os adaptadores do MoneyPrinterTurbo e de publicação nas plataformas são ligad
50
50
 
51
51
  O Thunderbolt mantém os idiomas históricos da criação de vídeos e acrescenta as dez opções canónicas do MoneyPrinterTurbo: **Inglês (en) 🇺🇸**, **Chinês Simplificado (zh) 🇨🇳**, **Alemão (de) 🇩🇪**, **Vietnamita (vi) 🇻🇳**, **Turco (tr) 🇹🇷**, **Português (pt) 🇧🇷**, **Russo (ru) 🇷🇺**, **Espanhol (es) 🇪🇸**, **Indonésio (id) 🇮🇩** e **Italiano (it) 🇮🇹**. Os códigos curtos são os valores persistidos e sincronizados com `ui.video_language` no `config.toml`; a bandeira e o nome são apenas a apresentação visual.
52
52
 
53
- No topo da área principal da aplicação existe o menu nativo de idioma no padrão do MoneyPrinterTurbo, sem sobrepor o toolbar do Streamlit. O selector mostra o idioma actual e cada opção aparece como **nome do idioma + bandeira**. Ao escolher outra opção, a preferência é guardada em `storage/state/settings.json` como `ui_language`, e a navegação lateral actualiza-se sem alterar o idioma seleccionado para os vídeos. O indicador de execução, o botão **Deploy** e o menu principal do Streamlit permanecem totalmente nativos e clicáveis.
53
+ No topo da área principal da aplicação existe o menu nativo de idioma no padrão do MoneyPrinterTurbo, sem sobrepor o toolbar do Streamlit. O selector mostra o rótulo visível **Language** acima do campo, cada opção aparece como **nome do idioma + código**, e a bandeira é uma imagem SVG local real, não um emoji ou sigla dependente da fonte do sistema. Ao escolher outra opção, a preferência é guardada em `storage/state/settings.json` como `ui_language`, e a navegação lateral e o dashboard inicial actualizam-se sem alterar o idioma seleccionado para os vídeos. O indicador de execução, o botão **Deploy** e o menu principal do Streamlit permanecem totalmente nativos e clicáveis.
54
54
 
55
- ## Navegação da UI 0.2.81
55
+ ## Navegação da UI 0.2.82
56
56
 
57
57
  A barra lateral mantém os níveis principais, nesta ordem: **Início**, **Niche Finder**, **Pipeline**, **Pipeline TikTok**, **Automação**, **Edição**, **AI Influencers** e **Configurações**. **Pipeline** é expansível e contém **Criação de Vídeos**, **Criação de Músicas**, **Roteiros** e **Upload**. **Automação** também é expansível e contém **Automação Youtube**. **Edição** é expansível e contém **Limpador de Metadados**, **Cortes**, **Editor Python** e **Download Mídia**, nessa ordem. **AI Influencers** é expansível e contém **Personagens**, **Redes Sociais** e **Tutorial Meta**, nessa ordem. **Niche Finder** é expansível e contém **Niche Finder Kaggle** e **Niche Finder Apify**. **Configurações** é expansível e contém **Canais Youtube**, **Blueprints Youtube**, **MCP**, **Contas Google**, **Configuração API** e **Notificações**. O Início reúne o dashboard e as filas do Pipeline, sem botões de acções rápidas.
58
58
 
package/app/main.py CHANGED
@@ -37,7 +37,7 @@ from hermes_ui.material_sources import material_api_keys, material_source_catalo
37
37
  from hermes_ui.music import list_music_files, materialize_suno_audio, request_suno_generation, store_music_file
38
38
  from hermes_ui.media_downloader import AUDIO_FORMATS, VIDEO_CONTAINERS, VIDEO_QUALITY_OPTIONS, MediaDownloadError, build_download_options, clear_media_download_history, dependency_status, download_media, list_media_downloads, media_download_file
39
39
  from hermes_ui.notifications import clear_notifications, list_notifications, mark_all_notifications_read, mark_notification_read, notification_event_catalog, notification_preferences, record_notification, reconcile_persisted_notifications, save_notification_preferences, unread_notification_count
40
- from hermes_ui.languages import LANGUAGE_CODES, language_code, language_label, ui_text, video_language_label, video_language_options
40
+ from hermes_ui.languages import LANGUAGE_CODES, LANGUAGE_FLAG_DATA_URIS, language_code, language_label, ui_language_menu_label, ui_text, video_language_label, video_language_options
41
41
  from hermes_ui.script_documents import list_script_documents, read_script_document, save_script_document, script_storage_path
42
42
  from hermes_ui.script_generation import generate_script_document
43
43
  from hermes_ui.voice_preview import DEFAULT_SAMPLE, load_preview_file, synthesize_preview
@@ -246,17 +246,31 @@ def save_video_language(value: str) -> str:
246
246
 
247
247
 
248
248
  def render_ui_language_picker(language: str) -> None:
249
- """Render the MoneyPrinterTurbo-style native language menu in app layout."""
249
+ """Render the native MoneyPrinterTurbo-style picker with local SVG flags."""
250
250
  current = language_code(language)
251
+ current_flag_uri = LANGUAGE_FLAG_DATA_URIS[current]
252
+ option_flag_css = "\n".join(
253
+ f'''[role="listbox"][aria-label="Language"] [role="option"][aria-posinset="{position}"] [data-item-hl]::before {{ content: ""; display: inline-block; width: 1.5rem; height: 1rem; margin-right: .55rem; flex: 0 0 auto; vertical-align: middle; background: url("{LANGUAGE_FLAG_DATA_URIS[code]}") center / cover no-repeat; border-radius: 2px; box-shadow: 0 0 0 1px rgba(255,255,255,.18); }}'''
254
+ for position, code in enumerate(LANGUAGE_CODES, start=1)
255
+ )
256
+ st.markdown(
257
+ f'''<style>
258
+ [data-testid="stSelectbox"]:has(input[aria-label="Language"]) label {{ display: block !important; visibility: visible !important; }}
259
+ [data-testid="stSelectbox"]:has(input[aria-label="Language"]) input {{ background-image: url("{current_flag_uri}") !important; background-repeat: no-repeat !important; background-position: .65rem center !important; background-size: 1.5rem 1rem !important; padding-left: 2.55rem !important; }}
260
+ [role="listbox"][aria-label="Language"] [data-item-hl] {{ display: flex; align-items: center; }}
261
+ {option_flag_css}
262
+ </style>''',
263
+ unsafe_allow_html=True,
264
+ )
251
265
  _spacer, language_col = st.columns([5.2, 1.5])
252
266
  with language_col:
253
267
  selected = st.selectbox(
254
- "Language / 语言",
268
+ "Language",
255
269
  list(LANGUAGE_CODES),
256
270
  index=list(LANGUAGE_CODES).index(current),
257
- format_func=language_label,
271
+ format_func=ui_language_menu_label,
258
272
  key="top_language_code_selector",
259
- label_visibility="collapsed",
273
+ label_visibility="visible",
260
274
  )
261
275
  if selected != current:
262
276
  save_ui_language(selected)
@@ -700,16 +714,26 @@ def render_channel_edit_form(channel: dict, youtube_account_ids: list[str], yout
700
714
 
701
715
 
702
716
  def render_dashboard():
717
+ ui_language = current_ui_language()
703
718
  st.title("Thunderbolt")
704
- st.caption("Interface local para operação e automação de conteúdo faceless")
719
+ st.caption(ui_text("Interface local para operação e automação de conteúdo faceless", ui_language))
705
720
  summary = pipeline_summary()
721
+ active_note = f'{summary["active_channels"]} {ui_text("activos", ui_language)}'
722
+ cards = [
723
+ ("Canais", summary["channels"], active_note),
724
+ ("Tarefas", summary["total_tasks"], ui_text("total registado", ui_language)),
725
+ ("A fazer", summary["pending"], ui_text("na pipeline", ui_language)),
726
+ ("Em execução", summary["doing"], ui_text("a decorrer", ui_language)),
727
+ ("Concluídos", summary["done"], ui_text("artefactos prontos", ui_language)),
728
+ ("Falhas", summary["failed"], ui_text("requerem atenção", ui_language)),
729
+ ]
706
730
  cols = st.columns(6)
707
- for col, (label, value, note) in zip(cols, [("Canais", summary["channels"], f'{summary["active_channels"]} activos'), ("Tarefas", summary["total_tasks"], "total registado"), ("A fazer", summary["pending"], "na pipeline"), ("Em execução", summary["doing"], "a decorrer"), ("Concluídos", summary["done"], "artefactos prontos"), ("Falhas", summary["failed"], "requerem atenção")]):
731
+ for col, (label, value, note) in zip(cols, cards):
708
732
  with col:
709
- card(label, value, note)
733
+ card(ui_text(label, ui_language), value, note)
710
734
  st.divider()
711
- st.subheader("Pipeline")
712
- st.caption("Filas locais e dependências da cascata")
735
+ st.subheader(ui_text("Pipeline", ui_language))
736
+ st.caption(ui_text("Filas locais e dependências da cascata", ui_language))
713
737
  queues = read_json("queues.json", {})
714
738
  blueprint_count = len(list_blueprint_files())
715
739
  for row_start in range(0, len(STAGES), 3):
@@ -717,9 +741,10 @@ def render_dashboard():
717
741
  for col, stage in zip(queue_cols, STAGES[row_start:row_start + 3]):
718
742
  with col:
719
743
  if stage == "blueprint":
720
- card("Blueprints", blueprint_count, f"na biblioteca · {len(queues.get(stage, []))} tarefa(s) na fila")
744
+ queue_note = f'{ui_text("na biblioteca", ui_language)} · {len(queues.get(stage, []))} {ui_text("tarefa(s) na fila", ui_language)}'
745
+ card(ui_text("Blueprints", ui_language), blueprint_count, queue_note)
721
746
  else:
722
- card(stage.title(), len(queues.get(stage, [])), "fila")
747
+ card(ui_text(stage.title(), ui_language), len(queues.get(stage, [])), ui_text("fila", ui_language))
723
748
 
724
749
 
725
750
  def render_blueprints():
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import base64
5
6
  from typing import Any
6
7
 
7
8
 
@@ -22,53 +23,38 @@ LANGUAGE_CATALOG: tuple[dict[str, str], ...] = (
22
23
  LANGUAGE_BY_CODE = {item["code"]: item for item in LANGUAGE_CATALOG}
23
24
  LANGUAGE_CODES = tuple(item["code"] for item in LANGUAGE_CATALOG)
24
25
 
25
- # Legacy labels used by existing channels, scripts and tests. They remain
26
+ # SVGs are embedded locally so flags render as images instead of relying on the
27
+ # host operating system's emoji font (which may show regional-letter siglas).
28
+ LANGUAGE_FLAG_SVGS: dict[str, str] = {
29
+ "en": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><rect width="30" height="20" fill="#fff"/><path fill="#b22234" d="M0 0h30v2H0zm0 4h30v2H0zm0 4h30v2H0zm0 4h30v2H0zm0 4h30v2H0zm0 4h30v2H0z"/><rect width="13" height="10.8" fill="#3c3b6e"/><g fill="#fff"><circle cx="2" cy="2" r=".55"/><circle cx="5" cy="2" r=".55"/><circle cx="8" cy="2" r=".55"/><circle cx="11" cy="2" r=".55"/><circle cx="3.5" cy="4.2" r=".55"/><circle cx="6.5" cy="4.2" r=".55"/><circle cx="9.5" cy="4.2" r=".55"/><circle cx="2" cy="6.4" r=".55"/><circle cx="5" cy="6.4" r=".55"/><circle cx="8" cy="6.4" r=".55"/><circle cx="11" cy="6.4" r=".55"/><circle cx="3.5" cy="8.6" r=".55"/><circle cx="6.5" cy="8.6" r=".55"/><circle cx="9.5" cy="8.6" r=".55"/></g></svg>',
30
+ "zh": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><rect width="30" height="20" fill="#de2910"/><path fill="#ffde00" d="m6 2 1 2.1 2.3.2-1.8 1.5.6 2.2L6 6.8 3.9 8l.6-2.2-1.8-1.5L5 4.1z"/></svg>',
31
+ "de": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><path fill="#000" d="M0 0h30v6.67H0z"/><path fill="#d00" d="M0 6.67h30v6.66H0z"/><path fill="#ffce00" d="M0 13.33h30V20H0z"/></svg>',
32
+ "vi": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><rect width="30" height="20" fill="#da251d"/><path fill="#ff0" d="m15 3 1.9 5.7h6l-4.9 3.5 1.9 5.8-4.9-3.6 1.9-5.8-4.9-3.5h6z"/></svg>',
33
+ "tr": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><rect width="30" height="20" fill="#e30a17"/><circle cx="13" cy="10" r="5.2" fill="#fff"/><circle cx="15" cy="10" r="4.2" fill="#e30a17"/><path fill="#fff" d="m19 6.1 1.2 3.1 3.3.1-2.6 2 1 3.2-2.9-1.9-2.8 1.9 1-3.2-2.6-2 3.3-.1z"/></svg>',
34
+ "pt": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><rect width="30" height="20" fill="#ffdf00"/><path fill="#009c3b" d="m15 1.2 12.2 8.8L15 18.8 2.8 10z"/><circle cx="15" cy="10" r="4.1" fill="#002776"/><path fill="#fff" d="M11.4 9.4c2.2-1.1 4.7-.9 7.1.6l-.3 1.1c-2.2-1.4-4.4-1.6-6.5-.6z"/></svg>',
35
+ "ru": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><path fill="#fff" d="M0 0h30v6.67H0z"/><path fill="#22408c" d="M0 6.67h30v6.66H0z"/><path fill="#d52b1e" d="M0 13.33h30V20H0z"/></svg>',
36
+ "es": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><path fill="#aa151b" d="M0 0h30v4H0zM0 16h30v4H0z"/><rect y="4" width="30" height="12" fill="#f1bf00"/><path fill="#ad1519" d="M7 7h2v6H7z"/></svg>',
37
+ "id": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><path fill="#ce1126" d="M0 0h30v10H0z"/><path fill="#fff" d="M0 10h30v10H0z"/></svg>',
38
+ "it": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20"><path fill="#009246" d="M0 0h10v20H0z"/><path fill="#f1f2f1" d="M10 0h10v20H10z"/><path fill="#ce2b37" d="M20 0h10v20H20z"/></svg>',
39
+ }
40
+ LANGUAGE_FLAG_DATA_URIS = {
41
+ code: "data:image/svg+xml;base64," + base64.b64encode(svg.encode("utf-8")).decode("ascii")
42
+ for code, svg in LANGUAGE_FLAG_SVGS.items()
43
+ }
44
+
45
+ # Legacy labels used by existing channels, scripts and tests. They remain
26
46
  # readable while new UI selections persist only the canonical short code.
27
47
  LEGACY_LANGUAGE_CODES = {
28
- "01 – inglês": "en",
29
- "01 - inglês": "en",
30
- "06alemão": "de",
31
- "06 - alemão": "de",
32
- "15italiano": "it",
33
- "15 - italiano": "it",
34
- "29 turco": "tr",
35
- "29 - turco": "tr",
36
- "31 russo": "ru",
37
- "31 - russo": "ru",
38
- "36 – português (brasil)": "pt",
39
- "36 - português (brasil)": "pt",
40
- "39 – mandarim": "zh",
41
- "39 - mandarim": "zh",
42
- "13 – espanhol (espanha)": "es",
43
- "41 - espanhol (latam)": "es",
44
- "42 – vietnamita": "vi",
45
- "42 - vietnamita": "vi",
46
- "44 – indonésio": "id",
47
- "44 - indonésio": "id",
48
- "13 – espanhol (espanha)": "es",
49
- "13 - espanhol (espanha)": "es",
50
- "37 – cantonês": "zh",
51
- "37 - cantonês": "zh",
52
- "english": "en",
53
- "inglês": "en",
54
- "inglés": "en",
55
- "zh": "zh",
56
- "chinês": "zh",
57
- "chinês simplificado": "zh",
58
- "deutsch": "de",
59
- "alemão": "de",
60
- "vietnamita": "vi",
61
- "turco": "tr",
62
- "português": "pt",
63
- "português (brasil)": "pt",
64
- "portuguese": "pt",
65
- "ru": "ru",
66
- "russo": "ru",
67
- "español": "es",
68
- "espanhol": "es",
69
- "indonésio": "id",
70
- "bahasa indonesia": "id",
71
- "italiano": "it",
48
+ "01 – inglês": "en", "01 - inglês": "en", "06 – alemão": "de", "06 - alemão": "de",
49
+ "15 – italiano": "it", "15 - italiano": "it", "29 – turco": "tr", "29 - turco": "tr",
50
+ "31russo": "ru", "31 - russo": "ru", "36 – português (brasil)": "pt", "36 - português (brasil)": "pt",
51
+ "39 – mandarim": "zh", "39 - mandarim": "zh", "13 – espanhol (espanha)": "es", "13 - espanhol (espanha)": "es",
52
+ "41 - espanhol (latam)": "es", "42 vietnamita": "vi", "42 - vietnamita": "vi",
53
+ "44 – indonésio": "id", "44 - indonésio": "id", "37 – cantonês": "zh", "37 - cantonês": "zh",
54
+ "english": "en", "inglês": "en", "inglés": "en", "zh": "zh", "chinês": "zh", "chinês simplificado": "zh",
55
+ "deutsch": "de", "alemão": "de", "vietnamita": "vi", "turco": "tr", "português": "pt",
56
+ "português (brasil)": "pt", "portuguese": "pt", "ru": "ru", "russo": "ru", "español": "es",
57
+ "espanhol": "es", "indonésio": "id", "bahasa indonesia": "id", "italiano": "it",
72
58
  }
73
59
 
74
60
 
@@ -87,13 +73,21 @@ def language_code(value: Any, default: str = "pt") -> str:
87
73
 
88
74
 
89
75
  def language_label(value: Any, *, include_code: bool = True) -> str:
90
- """Return the visual label with the requested flag and optional code."""
76
+ """Return a text label with the legacy emoji flag presentation."""
91
77
  code = language_code(value)
92
78
  item = LANGUAGE_BY_CODE[code]
93
79
  suffix = f" ({item['code']})" if include_code else ""
94
80
  return f"{item['name']}{suffix} {item['flag']}"
95
81
 
96
82
 
83
+ def ui_language_menu_label(value: Any, *, include_code: bool = True) -> str:
84
+ """Return text-only UI label; the picker renders the flag as a real SVG icon."""
85
+ code = language_code(value)
86
+ item = LANGUAGE_BY_CODE[code]
87
+ suffix = f" ({item['code']})" if include_code else ""
88
+ return f"{item['name']}{suffix}"
89
+
90
+
97
91
  def language_option_labels(*, include_code: bool = True) -> list[str]:
98
92
  return [language_label(item["code"], include_code=include_code) for item in LANGUAGE_CATALOG]
99
93
 
@@ -102,51 +96,47 @@ def language_option_codes() -> list[str]:
102
96
  return list(LANGUAGE_CODES)
103
97
 
104
98
 
99
+ _CORE_UI_TEXT_KEYS = (
100
+ "Início", "Niche Finder", "Pipeline", "Pipeline TikTok", "Automação", "Edição", "AI Influencers", "Configurações",
101
+ "Criação de Vídeos", "Criação de Músicas", "Roteiros", "Upload", "Prompts Master", "Contas TikTok", "Automação Youtube",
102
+ "Niche Finder Kaggle", "Niche Finder Apify", "Limpador de Metadados", "Cortes", "Editor Python", "Download Mídia",
103
+ "Personagens", "Redes Sociais", "Tutorial Meta", "Canais Youtube", "Blueprints Youtube", "MCP", "Contas Google",
104
+ "Configuração API", "Notificações", "Interface local para operação e automação de conteúdo faceless", "Canais", "activos",
105
+ "Tarefas", "total registado", "A fazer", "na pipeline", "Em execução", "a decorrer", "Concluídos", "artefactos prontos",
106
+ "Falhas", "requerem atenção", "Filas locais e dependências da cascata", "Niche", "Blueprints", "Brand", "Script", "Title",
107
+ "Thumbnail", "Video", "Edit", "fila", "na biblioteca", "tarefa(s) na fila", "Language",
108
+ )
109
+
110
+
105
111
  UI_TRANSLATIONS: dict[str, dict[str, str]] = {
112
+ "pt": {key: key for key in _CORE_UI_TEXT_KEYS},
106
113
  "en": {
107
- "Início": "Home", "Niche Finder": "Niche Finder", "Pipeline": "Pipeline", "Pipeline TikTok": "TikTok Pipeline",
108
- "Automação": "Automation", "Edição": "Editing", "AI Influencers": "AI Influencers", "Configurações": "Settings",
109
- "Criação de Vídeos": "Video Creation", "Criação de Músicas": "Music Creation", "Roteiros": "Scripts", "Upload": "Upload",
110
- "Prompts Master": "Prompt Masters", "Contas TikTok": "TikTok Accounts", "Automação Youtube": "YouTube Automation",
111
- "Canais Youtube": "YouTube Channels", "Blueprints Youtube": "YouTube Blueprints", "Contas Google": "Google Accounts",
112
- "Configuração API": "API Configuration", "Notificações": "Notifications", "Niche Finder Kaggle": "Niche Finder Kaggle",
113
- "Niche Finder Apify": "Niche Finder Apify", "Limpador de Metadados": "Metadata Cleaner", "Cortes": "Cuts",
114
- "Editor Python": "Python Editor", "Download Mídia": "Media Download", "Personagens": "Characters", "Redes Sociais": "Social Networks",
115
- "Tutorial Meta": "Meta Tutorial", "Idioma da interface": "Interface language",
114
+ "Início": "Home", "Niche Finder": "Niche Finder", "Pipeline": "Pipeline", "Pipeline TikTok": "TikTok Pipeline", "Automação": "Automation", "Edição": "Editing", "AI Influencers": "AI Influencers", "Configurações": "Settings", "Criação de Vídeos": "Video Creation", "Criação de Músicas": "Music Creation", "Roteiros": "Scripts", "Upload": "Upload", "Prompts Master": "Prompt Masters", "Contas TikTok": "TikTok Accounts", "Automação Youtube": "YouTube Automation", "Niche Finder Kaggle": "Niche Finder Kaggle", "Niche Finder Apify": "Niche Finder Apify", "Limpador de Metadados": "Metadata Cleaner", "Cortes": "Cuts", "Editor Python": "Python Editor", "Download Mídia": "Media Download", "Personagens": "Characters", "Redes Sociais": "Social Networks", "Tutorial Meta": "Meta Tutorial", "Canais Youtube": "YouTube Channels", "Blueprints Youtube": "YouTube Blueprints", "MCP": "MCP", "Contas Google": "Google Accounts", "Configuração API": "API Configuration", "Notificações": "Notifications", "Interface local para operação e automação de conteúdo faceless": "Local interface for faceless content operation and automation", "Canais": "Channels", "activos": "active", "Tarefas": "Tasks", "total registado": "total registered", "A fazer": "To do", "na pipeline": "in pipeline", "Em execução": "Running", "a decorrer": "in progress", "Concluídos": "Completed", "artefactos prontos": "ready artifacts", "Falhas": "Failures", "requerem atenção": "need attention", "Filas locais e dependências da cascata": "Local queues and cascade dependencies", "Niche": "Niche", "Blueprints": "Blueprints", "Brand": "Brand", "Script": "Script", "Title": "Title", "Thumbnail": "Thumbnail", "Video": "Video", "Edit": "Edit", "fila": "queue", "na biblioteca": "in library", "tarefa(s) na fila": "task(s) in queue", "Language": "Language",
116
115
  },
117
116
  "zh": {
118
- "Início": "首页", "Pipeline": "视频流程", "Pipeline TikTok": "TikTok 流程", "Automação": "自动化", "Edição": "编辑", "Configurações": "设置",
119
- "Criação de Vídeos": "视频创作", "Criação de Músicas": "音乐创作", "Roteiros": "脚本", "Upload": "上传", "Contas Google": "Google 账户", "Notificações": "通知", "Idioma da interface": "界面语言",
117
+ "Início": "首页", "Niche Finder": "利基搜索", "Pipeline": "视频流程", "Pipeline TikTok": "TikTok 流程", "Automação": "自动化", "Edição": "编辑", "AI Influencers": "AI 影响者", "Configurações": "设置", "Criação de Vídeos": "视频创作", "Criação de Músicas": "音乐创作", "Roteiros": "脚本", "Upload": "上传", "Prompts Master": "主提示词", "Contas TikTok": "TikTok 账户", "Automação Youtube": "YouTube 自动化", "Niche Finder Kaggle": "Kaggle 利基搜索", "Niche Finder Apify": "Apify 利基搜索", "Limpador de Metadados": "元数据清理器", "Cortes": "剪辑", "Editor Python": "Python 编辑器", "Download Mídia": "媒体下载", "Personagens": "角色", "Redes Sociais": "社交网络", "Tutorial Meta": "Meta 教程", "Canais Youtube": "YouTube 频道", "Blueprints Youtube": "YouTube 蓝图", "MCP": "MCP", "Contas Google": "Google 账户", "Configuração API": "API 配置", "Notificações": "通知", "Interface local para operação e automação de conteúdo faceless": "无脸内容运营与自动化本地界面", "Canais": "频道", "activos": "活跃", "Tarefas": "任务", "total registado": "已登记总数", "A fazer": "待处理", "na pipeline": "在流程中", "Em execução": "执行中", "a decorrer": "正在进行", "Concluídos": "已完成", "artefactos prontos": "成品已就绪", "Falhas": "失败", "requerem atenção": "需要关注", "Filas locais e dependências da cascata": "本地队列和级联依赖", "Niche": "利基", "Blueprints": "蓝图", "Brand": "品牌", "Script": "脚本", "Title": "标题", "Thumbnail": "缩略图", "Video": "视频", "Edit": "编辑", "fila": "队列", "na biblioteca": "在库中", "tarefa(s) na fila": "队列中的任务", "Language": "语言",
120
118
  },
121
119
  "de": {
122
- "Início": "Startseite", "Pipeline": "Pipeline", "Pipeline TikTok": "TikTok-Pipeline", "Automação": "Automatisierung", "Edição": "Bearbeitung", "Configurações": "Einstellungen",
123
- "Criação de Vídeos": "Videoerstellung", "Criação de Músicas": "Musikerstellung", "Roteiros": "Skripte", "Upload": "Upload", "Contas Google": "Google-Konten", "Notificações": "Benachrichtigungen", "Idioma da interface": "Oberflächensprache",
120
+ "Início": "Startseite", "Niche Finder": "Nischenfinder", "Pipeline": "Pipeline", "Pipeline TikTok": "TikTok-Pipeline", "Automação": "Automatisierung", "Edição": "Bearbeitung", "AI Influencers": "KI-Influencer", "Configurações": "Einstellungen", "Criação de Vídeos": "Videoerstellung", "Criação de Músicas": "Musikerstellung", "Roteiros": "Skripte", "Upload": "Upload", "Prompts Master": "Prompt Masters", "Contas TikTok": "TikTok-Konten", "Automação Youtube": "YouTube-Automatisierung", "Niche Finder Kaggle": "Kaggle-Nischenfinder", "Niche Finder Apify": "Apify-Nischenfinder", "Limpador de Metadados": "Metadatenbereinigung", "Cortes": "Schnitte", "Editor Python": "Python-Editor", "Download Mídia": "Mediendownload", "Personagens": "Figuren", "Redes Sociais": "Soziale Netzwerke", "Tutorial Meta": "Meta-Tutorial", "Canais Youtube": "YouTube-Kanäle", "Blueprints Youtube": "YouTube-Blueprints", "MCP": "MCP", "Contas Google": "Google-Konten", "Configuração API": "API-Konfiguration", "Notificações": "Benachrichtigungen", "Interface local para operação e automação de conteúdo faceless": "Lokale Oberfläche für den Betrieb und die Automatisierung von Faceless-Inhalten", "Canais": "Kanäle", "activos": "aktiv", "Tarefas": "Aufgaben", "total registado": "insgesamt registriert", "A fazer": "Offen", "na pipeline": "in der Pipeline", "Em execução": "In Ausführung", "a decorrer": "in Bearbeitung", "Concluídos": "Abgeschlossen", "artefactos prontos": "fertige Artefakte", "Falhas": "Fehler", "requerem atenção": "benötigen Aufmerksamkeit", "Filas locais e dependências da cascata": "Lokale Warteschlangen und Abhängigkeiten", "Niche": "Nische", "Blueprints": "Blueprints", "Brand": "Marke", "Script": "Skript", "Title": "Titel", "Thumbnail": "Thumbnail", "Video": "Video", "Edit": "Bearbeitung", "fila": "Warteschlange", "na biblioteca": "in der Bibliothek", "tarefa(s) na fila": "Aufgabe(n) in der Warteschlange", "Language": "Sprache",
124
121
  },
125
122
  "vi": {
126
- "Início": "Trang chủ", "Pipeline": "Quy trình", "Pipeline TikTok": "Quy trình TikTok", "Automação": "Tự động hóa", "Edição": "Chỉnh sửa", "Configurações": "Cài đặt",
127
- "Criação de Vídeos": "Tạo video", "Criação de Músicas": "Tạo nhạc", "Roteiros": "Kịch bản", "Upload": "Tải lên", "Contas Google": "Tài khoản Google", "Notificações": "Thông báo", "Idioma da interface": "Ngôn ngữ giao diện",
123
+ "Início": "Trang chủ", "Niche Finder": "Tìm ngách", "Pipeline": "Quy trình", "Pipeline TikTok": "Quy trình TikTok", "Automação": "Tự động hóa", "Edição": "Chỉnh sửa", "AI Influencers": "Người ảnh hưởng AI", "Configurações": "Cài đặt", "Criação de Vídeos": "Tạo video", "Criação de Músicas": "Tạo nhạc", "Roteiros": "Kịch bản", "Upload": "Tải lên", "Prompts Master": "Prompt Master", "Contas TikTok": "Tài khoản TikTok", "Automação Youtube": "Tự động hóa YouTube", "Niche Finder Kaggle": "Tìm ngách Kaggle", "Niche Finder Apify": "Tìm ngách Apify", "Limpador de Metadados": "Trình dọn siêu dữ liệu", "Cortes": "Cắt", "Editor Python": "Trình soạn thảo Python", "Download Mídia": "Tải phương tiện", "Personagens": "Nhân vật", "Redes Sociais": "Mạng xã hội", "Tutorial Meta": "Hướng dẫn Meta", "Canais Youtube": "Kênh YouTube", "Blueprints Youtube": "Blueprint YouTube", "MCP": "MCP", "Contas Google": "Tài khoản Google", "Configuração API": "Cấu hình API", "Notificações": "Thông báo", "Interface local para operação e automação de conteúdo faceless": "Giao diện cục bộ để vận hành và tự động hóa nội dung faceless", "Canais": "Kênh", "activos": "đang hoạt động", "Tarefas": "Tác vụ", "total registado": "tổng số đã đăng ký", "A fazer": "Cần làm", "na pipeline": "trong quy trình", "Em execução": "Đang chạy", "a decorrer": "đang diễn ra", "Concluídos": "Đã hoàn tất", "artefactos prontos": "sản phẩm đã sẵn sàng", "Falhas": "Lỗi", "requerem atenção": "cần chú ý", "Filas locais e dependências da cascata": "Hàng đợi cục bộ và các phụ thuộc", "Niche": "Ngách", "Blueprints": "Blueprint", "Brand": "Thương hiệu", "Script": "Kịch bản", "Title": "Tiêu đề", "Thumbnail": "Ảnh thu nhỏ", "Video": "Video", "Edit": "Chỉnh sửa", "fila": "hàng đợi", "na biblioteca": "trong thư viện", "tarefa(s) na fila": "tác vụ trong hàng đợi", "Language": "Ngôn ngữ",
128
124
  },
129
125
  "tr": {
130
- "Início": "Ana sayfa", "Pipeline": "Akış", "Pipeline TikTok": "TikTok akışı", "Automação": "Otomasyon", "Edição": "Düzenleme", "Configurações": "Ayarlar",
131
- "Criação de Vídeos": "Video oluşturma", "Criação de Músicas": "Müzik oluşturma", "Roteiros": "Senaryolar", "Upload": "Yükleme", "Contas Google": "Google hesapları", "Notificações": "Bildirimler", "Idioma da interface": "Arayüz dili",
126
+ "Início": "Ana sayfa", "Niche Finder": "Niş Bulucu", "Pipeline": "Akış", "Pipeline TikTok": "TikTok Akışı", "Automação": "Otomasyon", "Edição": "Düzenleme", "AI Influencers": "Yapay Zekâ Etkileyicileri", "Configurações": "Ayarlar", "Criação de Vídeos": "Video Oluşturma", "Criação de Músicas": "Müzik Oluşturma", "Roteiros": "Senaryolar", "Upload": "Yükleme", "Prompts Master": "Prompt Master", "Contas TikTok": "TikTok Hesapları", "Automação Youtube": "YouTube Otomasyonu", "Niche Finder Kaggle": "Kaggle Niş Bulucu", "Niche Finder Apify": "Apify Niş Bulucu", "Limpador de Metadados": "Meta Veri Temizleyici", "Cortes": "Kesitler", "Editor Python": "Python Editörü", "Download Mídia": "Medya İndirme", "Personagens": "Karakterler", "Redes Sociais": "Sosyal Ağlar", "Tutorial Meta": "Meta Eğitimi", "Canais Youtube": "YouTube Kanalları", "Blueprints Youtube": "YouTube Blueprint'leri", "MCP": "MCP", "Contas Google": "Google Hesapları", "Configuração API": "API Yapılandırması", "Notificações": "Bildirimler", "Interface local para operação e automação de conteúdo faceless": "Faceless içerik operasyonu ve otomasyonu için yerel arayüz", "Canais": "Kanallar", "activos": "aktif", "Tarefas": "Görevler", "total registado": "kayıtlı toplam", "A fazer": "Yapılacak", "na pipeline": "akışta", "Em execução": "Çalışıyor", "a decorrer": "devam ediyor", "Concluídos": "Tamamlandı", "artefactos prontos": "hazır çıktılar", "Falhas": "Hatalar", "requerem atenção": "dikkat gerekiyor", "Filas locais e dependências da cascata": "Yerel kuyruklar ve bağımlılıklar", "Niche": "Niş", "Blueprints": "Blueprint", "Brand": "Marka", "Script": "Senaryo", "Title": "Başlık", "Thumbnail": "Küçük resim", "Video": "Video", "Edit": "Düzenle", "fila": "kuyruk", "na biblioteca": "kütüphanede", "tarefa(s) na fila": "kuyruktaki görev(ler)", "Language": "Dil",
132
127
  },
133
128
  "ru": {
134
- "Início": "Главная", "Pipeline": "Конвейер", "Pipeline TikTok": "Конвейер TikTok", "Automação": "Автоматизация", "Edição": "Редактирование", "Configurações": "Настройки",
135
- "Criação de Vídeos": "Создание видео", "Criação de Músicas": "Создание музыки", "Roteiros": "Сценарии", "Upload": "Загрузка", "Contas Google": "Аккаунты Google", "Notificações": "Уведомления", "Idioma da interface": "Язык интерфейса",
129
+ "Início": "Главная", "Niche Finder": "Поиск ниши", "Pipeline": "Конвейер", "Pipeline TikTok": "Конвейер TikTok", "Automação": "Автоматизация", "Edição": "Редактирование", "AI Influencers": "ИИ-инфлюенсеры", "Configurações": "Настройки", "Criação de Vídeos": "Создание видео", "Criação de Músicas": "Создание музыки", "Roteiros": "Сценарии", "Upload": "Загрузка", "Prompts Master": "Мастер-промпты", "Contas TikTok": "Аккаунты TikTok", "Automação Youtube": "Автоматизация YouTube", "Niche Finder Kaggle": "Поиск ниши Kaggle", "Niche Finder Apify": "Поиск ниши Apify", "Limpador de Metadados": "Очистка метаданных", "Cortes": "Нарезка", "Editor Python": "Редактор Python", "Download Mídia": "Загрузка медиа", "Personagens": "Персонажи", "Redes Sociais": "Социальные сети", "Tutorial Meta": "Руководство Meta", "Canais Youtube": "Каналы YouTube", "Blueprints Youtube": "Blueprint YouTube", "MCP": "MCP", "Contas Google": "Аккаунты Google", "Configuração API": "Настройка API", "Notificações": "Уведомления", "Interface local para operação e automação de conteúdo faceless": "Локальный интерфейс для работы и автоматизации faceless-контента", "Canais": "Каналы", "activos": "активны", "Tarefas": "Задачи", "total registado": "всего зарегистрировано", "A fazer": "К выполнению", "na pipeline": "в конвейере", "Em execução": "Выполняются", "a decorrer": "в процессе", "Concluídos": "Завершены", "artefactos prontos": "готовые материалы", "Falhas": "Ошибки", "requerem atenção": "требуют внимания", "Filas locais e dependências da cascata": "Локальные очереди и зависимости конвейера", "Niche": "Ниша", "Blueprints": "Blueprints", "Brand": "Бренд", "Script": "Сценарий", "Title": "Заголовок", "Thumbnail": "Миниатюра", "Video": "Видео", "Edit": "Монтаж", "fila": "очередь", "na biblioteca": "в библиотеке", "tarefa(s) na fila": "задач в очереди", "Language": "Язык",
136
130
  },
137
131
  "es": {
138
- "Início": "Inicio", "Pipeline": "Flujo", "Pipeline TikTok": "Flujo de TikTok", "Automação": "Automatización", "Edição": "Edición", "Configurações": "Configuración",
139
- "Criação de Vídeos": "Creación de vídeos", "Criação de Músicas": "Creación de música", "Roteiros": "Guiones", "Upload": "Carga", "Contas Google": "Cuentas de Google", "Notificações": "Notificaciones", "Idioma da interface": "Idioma de la interfaz",
132
+ "Início": "Inicio", "Niche Finder": "Buscador de nichos", "Pipeline": "Flujo", "Pipeline TikTok": "Flujo de TikTok", "Automação": "Automatización", "Edição": "Edición", "AI Influencers": "Influencers de IA", "Configurações": "Configuración", "Criação de Vídeos": "Creación de vídeos", "Criação de Músicas": "Creación de música", "Roteiros": "Guiones", "Upload": "Subir", "Prompts Master": "Prompts maestros", "Contas TikTok": "Cuentas de TikTok", "Automação Youtube": "Automatización de YouTube", "Niche Finder Kaggle": "Buscador de nichos Kaggle", "Niche Finder Apify": "Buscador de nichos Apify", "Limpador de Metadados": "Limpiador de metadatos", "Cortes": "Cortes", "Editor Python": "Editor de Python", "Download Mídia": "Descargar medios", "Personagens": "Personajes", "Redes Sociais": "Redes sociales", "Tutorial Meta": "Tutorial de Meta", "Canais Youtube": "Canales de YouTube", "Blueprints Youtube": "Blueprints de YouTube", "MCP": "MCP", "Contas Google": "Cuentas de Google", "Configuração API": "Configuración de API", "Notificações": "Notificaciones", "Interface local para operação e automação de conteúdo faceless": "Interfaz local para operar y automatizar contenido faceless", "Canais": "Canales", "activos": "activos", "Tarefas": "Tareas", "total registado": "total registrado", "A fazer": "Pendiente", "na pipeline": "en el flujo", "Em execução": "En ejecución", "a decorrer": "en curso", "Concluídos": "Completados", "artefactos prontos": "artefactos listos", "Falhas": "Fallos", "requerem atenção": "requieren atención", "Filas locais e dependências da cascata": "Colas locales y dependencias del flujo", "Niche": "Nicho", "Blueprints": "Blueprints", "Brand": "Marca", "Script": "Guion", "Title": "Título", "Thumbnail": "Miniatura", "Video": "Vídeo", "Edit": "Edición", "fila": "cola", "na biblioteca": "en la biblioteca", "tarefa(s) na fila": "tarea(s) en la cola", "Language": "Idioma",
140
133
  },
141
134
  "id": {
142
- "Início": "Beranda", "Pipeline": "Alur", "Pipeline TikTok": "Alur TikTok", "Automação": "Otomatisasi", "Edição": "Penyuntingan", "Configurações": "Pengaturan",
143
- "Criação de Vídeos": "Pembuatan video", "Criação de Músicas": "Pembuatan musik", "Roteiros": "Skrip", "Upload": "Unggah", "Contas Google": "Akun Google", "Notificações": "Notifikasi", "Idioma da interface": "Bahasa antarmuka",
135
+ "Início": "Beranda", "Niche Finder": "Pencari Niche", "Pipeline": "Alur", "Pipeline TikTok": "Alur TikTok", "Automação": "Otomatisasi", "Edição": "Penyuntingan", "AI Influencers": "Influencer AI", "Configurações": "Pengaturan", "Criação de Vídeos": "Pembuatan video", "Criação de Músicas": "Pembuatan musik", "Roteiros": "Skrip", "Upload": "Unggah", "Prompts Master": "Prompt Master", "Contas TikTok": "Akun TikTok", "Automação Youtube": "Otomatisasi YouTube", "Niche Finder Kaggle": "Pencari Niche Kaggle", "Niche Finder Apify": "Pencari Niche Apify", "Limpador de Metadados": "Pembersih metadata", "Cortes": "Potongan", "Editor Python": "Editor Python", "Download Mídia": "Unduh media", "Personagens": "Karakter", "Redes Sociais": "Jejaring sosial", "Tutorial Meta": "Tutorial Meta", "Canais Youtube": "Kanal YouTube", "Blueprints Youtube": "Blueprint YouTube", "MCP": "MCP", "Contas Google": "Akun Google", "Configuração API": "Konfigurasi API", "Notificações": "Notifikasi", "Interface local para operação e automação de conteúdo faceless": "Antarmuka lokal untuk operasi dan otomatisasi konten faceless", "Canais": "Kanal", "activos": "aktif", "Tarefas": "Tugas", "total registado": "total terdaftar", "A fazer": "Perlu dilakukan", "na pipeline": "dalam alur", "Em execução": "Berjalan", "a decorrer": "sedang berlangsung", "Concluídos": "Selesai", "artefactos prontos": "artefak siap", "Falhas": "Kegagalan", "requerem atenção": "perlu perhatian", "Filas locais e dependências da cascata": "Antrean lokal dan dependensi alur", "Niche": "Niche", "Blueprints": "Blueprint", "Brand": "Merek", "Script": "Skrip", "Title": "Judul", "Thumbnail": "Thumbnail", "Video": "Video", "Edit": "Edit", "fila": "antrean", "na biblioteca": "di pustaka", "tarefa(s) na fila": "tugas dalam antrean", "Language": "Bahasa",
144
136
  },
145
137
  "it": {
146
- "Início": "Home", "Pipeline": "Pipeline", "Pipeline TikTok": "Pipeline TikTok", "Automação": "Automazione", "Edição": "Modifica", "Configurações": "Impostazioni",
147
- "Criação de Vídeos": "Creazione video", "Criação de Músicas": "Creazione musicale", "Roteiros": "Script", "Upload": "Caricamento", "Contas Google": "Account Google", "Notificações": "Notifiche", "Idioma da interface": "Lingua dell'interfaccia",
138
+ "Início": "Home", "Niche Finder": "Trova nicchia", "Pipeline": "Pipeline", "Pipeline TikTok": "Pipeline TikTok", "Automação": "Automazione", "Edição": "Modifica", "AI Influencers": "Influencer AI", "Configurações": "Impostazioni", "Criação de Vídeos": "Creazione video", "Criação de Músicas": "Creazione musicale", "Roteiros": "Script", "Upload": "Caricamento", "Prompts Master": "Prompt Master", "Contas TikTok": "Account TikTok", "Automação Youtube": "Automazione YouTube", "Niche Finder Kaggle": "Trova nicchia Kaggle", "Niche Finder Apify": "Trova nicchia Apify", "Limpador de Metadados": "Pulizia metadati", "Cortes": "Tagli", "Editor Python": "Editor Python", "Download Mídia": "Download media", "Personagens": "Personaggi", "Redes Sociais": "Social network", "Tutorial Meta": "Tutorial Meta", "Canais Youtube": "Canali YouTube", "Blueprints Youtube": "Blueprint YouTube", "MCP": "MCP", "Contas Google": "Account Google", "Configuração API": "Configurazione API", "Notificações": "Notifiche", "Interface local para operação e automação de conteúdo faceless": "Interfaccia locale per la gestione e l'automazione di contenuti faceless", "Canais": "Canali", "activos": "attivi", "Tarefas": "Attività", "total registado": "totale registrato", "A fazer": "Da fare", "na pipeline": "nella pipeline", "Em execução": "In esecuzione", "a decorrer": "in corso", "Concluídos": "Completati", "artefactos prontos": "artefatti pronti", "Falhas": "Errori", "requerem atenção": "richiedono attenzione", "Filas locais e dependências da cascata": "Code locali e dipendenze della pipeline", "Niche": "Nicchia", "Blueprints": "Blueprint", "Brand": "Brand", "Script": "Script", "Title": "Titolo", "Thumbnail": "Miniatura", "Video": "Video", "Edit": "Modifica", "fila": "coda", "na biblioteca": "nella libreria", "tarefa(s) na fila": "attività in coda", "Language": "Lingua",
148
139
  },
149
- "pt": {},
150
140
  }
151
141
 
152
142
 
@@ -166,11 +156,7 @@ def language_locale(value: Any) -> str:
166
156
  def video_language_label(value: Any) -> str:
167
157
  """Format a video-language value while preserving all historical options."""
168
158
  raw = str(value or "").strip()
169
- if raw.casefold() in {
170
- "music",
171
- "00 – apenas música de fundo (sem falas)",
172
- "00 - apenas música de fundo (sem falas)",
173
- }:
159
+ if raw.casefold() in {"music", "00 – apenas música de fundo (sem falas)", "00 - apenas música de fundo (sem falas)"}:
174
160
  return "🎵 Apenas Música de Fundo (Sem Falas)"
175
161
  normalized = language_code(raw, default="")
176
162
  if normalized in LANGUAGE_BY_CODE and (raw.casefold() in LEGACY_LANGUAGE_CODES or raw in LANGUAGE_BY_CODE):
@@ -183,15 +169,7 @@ def video_language_options() -> list[str]:
183
169
 
184
170
 
185
171
  __all__ = [
186
- "LANGUAGE_CATALOG",
187
- "LANGUAGE_BY_CODE",
188
- "LANGUAGE_CODES",
189
- "language_code",
190
- "language_flag",
191
- "language_label",
192
- "language_locale",
193
- "language_option_codes",
194
- "language_option_labels",
195
- "video_language_label",
196
- "video_language_options",
172
+ "LANGUAGE_CATALOG", "LANGUAGE_BY_CODE", "LANGUAGE_CODES", "LANGUAGE_FLAG_DATA_URIS", "language_code", "language_flag",
173
+ "language_label", "ui_language_menu_label", "language_locale", "language_option_codes", "language_option_labels",
174
+ "ui_text", "video_language_label", "video_language_options",
197
175
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.2.81",
3
+ "version": "0.2.82",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",