From fcf0ab4d3beea77ece67d1b52ad172e2dd1a86e9 Mon Sep 17 00:00:00 2001 From: Rossi-Luciano Date: Sat, 10 Jan 2026 13:37:17 -0300 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20adiciona=20sistema=20de=20tradu?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20idiomas=20para=20menus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adiciona dicionário LANGUAGE_TRANSLATIONS com 40+ idiomas em choices.py - Adiciona função get_language_name() para traduzir códigos ISO - Adiciona função get_language_code_upper() para códigos ISO maiúsculos - Adiciona função register_language_filters() para registrar filtros Jinja2 - Usa g.interface_language (compatível com PR #366) - Suporta traduções de interface em pt/en/es Corrige: TK-355 --- opac/webapp/choices.py | 222 +++++++++++++++++++++++++++++++++++++ opac/webapp/utils/utils.py | 84 ++++++++++++++ 2 files changed, 306 insertions(+) mode change 100644 => 100755 opac/webapp/choices.py mode change 100644 => 100755 opac/webapp/utils/utils.py diff --git a/opac/webapp/choices.py b/opac/webapp/choices.py old mode 100644 new mode 100755 index 7b2dbd347..fb8361ea4 --- a/opac/webapp/choices.py +++ b/opac/webapp/choices.py @@ -19,6 +19,228 @@ "A&HCI": "Arts & Humanities Citation", } +# Dicionário expandido de traduções de idiomas +# Estrutura: {codigo_iso: {idioma_interface: nome_traduzido}} +# Usado para traduzir nomes de idiomas nos menus de artigos (resumo, texto, PDF) +LANGUAGE_TRANSLATIONS = { + 'pt': { + 'pt': __('Português'), + 'en': __('Portuguese'), + 'es': __('Portugués') + }, + 'en': { + 'pt': __('Inglês'), + 'en': __('English'), + 'es': __('Inglés') + }, + 'es': { + 'pt': __('Espanhol'), + 'en': __('Spanish'), + 'es': __('Español') + }, + 'de': { + 'pt': __('Alemão'), + 'en': __('German'), + 'es': __('Alemán') + }, + 'fr': { + 'pt': __('Francês'), + 'en': __('French'), + 'es': __('Francés') + }, + 'it': { + 'pt': __('Italiano'), + 'en': __('Italian'), + 'es': __('Italiano') + }, + 'ru': { + 'pt': __('Russo'), + 'en': __('Russian'), + 'es': __('Ruso') + }, + 'ja': { + 'pt': __('Japonês'), + 'en': __('Japanese'), + 'es': __('Japonés') + }, + 'zh': { + 'pt': __('Chinês'), + 'en': __('Chinese'), + 'es': __('Chino') + }, + 'cn': { # Alias para zh (compatibilidade com ISO3166_ALPHA2 existente) + 'pt': __('Chinês'), + 'en': __('Chinese'), + 'es': __('Chino') + }, + 'ar': { + 'pt': __('Árabe'), + 'en': __('Arabic'), + 'es': __('Árabe') + }, + 'ko': { + 'pt': __('Coreano'), + 'en': __('Korean'), + 'es': __('Coreano') + }, + 'nl': { + 'pt': __('Holandês'), + 'en': __('Dutch'), + 'es': __('Holandés') + }, + 'pl': { + 'pt': __('Polonês'), + 'en': __('Polish'), + 'es': __('Polaco') + }, + 'tr': { + 'pt': __('Turco'), + 'en': __('Turkish'), + 'es': __('Turco') + }, + 'sv': { + 'pt': __('Sueco'), + 'en': __('Swedish'), + 'es': __('Sueco') + }, + 'da': { + 'pt': __('Dinamarquês'), + 'en': __('Danish'), + 'es': __('Danés') + }, + 'no': { + 'pt': __('Norueguês'), + 'en': __('Norwegian'), + 'es': __('Noruego') + }, + 'fi': { + 'pt': __('Finlandês'), + 'en': __('Finnish'), + 'es': __('Finlandés') + }, + 'cs': { + 'pt': __('Tcheco'), + 'en': __('Czech'), + 'es': __('Checo') + }, + 'el': { + 'pt': __('Grego'), + 'en': __('Greek'), + 'es': __('Griego') + }, + 'he': { + 'pt': __('Hebraico'), + 'en': __('Hebrew'), + 'es': __('Hebreo') + }, + 'hi': { + 'pt': __('Hindi'), + 'en': __('Hindi'), + 'es': __('Hindi') + }, + 'th': { + 'pt': __('Tailandês'), + 'en': __('Thai'), + 'es': __('Tailandés') + }, + 'vi': { + 'pt': __('Vietnamita'), + 'en': __('Vietnamese'), + 'es': __('Vietnamita') + }, + 'id': { + 'pt': __('Indonésio'), + 'en': __('Indonesian'), + 'es': __('Indonesio') + }, + 'ms': { + 'pt': __('Malaio'), + 'en': __('Malay'), + 'es': __('Malayo') + }, + 'ca': { + 'pt': __('Catalão'), + 'en': __('Catalan'), + 'es': __('Catalán') + }, + 'gl': { + 'pt': __('Galego'), + 'en': __('Galician'), + 'es': __('Gallego') + }, + 'eu': { + 'pt': __('Basco'), + 'en': __('Basque'), + 'es': __('Vasco') + }, + 'ro': { + 'pt': __('Romeno'), + 'en': __('Romanian'), + 'es': __('Rumano') + }, + 'hu': { + 'pt': __('Húngaro'), + 'en': __('Hungarian'), + 'es': __('Húngaro') + }, + 'uk': { + 'pt': __('Ucraniano'), + 'en': __('Ukrainian'), + 'es': __('Ucraniano') + }, + 'hr': { + 'pt': __('Croata'), + 'en': __('Croatian'), + 'es': __('Croata') + }, + 'sr': { + 'pt': __('Sérvio'), + 'en': __('Serbian'), + 'es': __('Serbio') + }, + 'bg': { + 'pt': __('Búlgaro'), + 'en': __('Bulgarian'), + 'es': __('Búlgaro') + }, + 'sk': { + 'pt': __('Eslovaco'), + 'en': __('Slovak'), + 'es': __('Eslovaco') + }, + 'sl': { + 'pt': __('Esloveno'), + 'en': __('Slovenian'), + 'es': __('Esloveno') + }, + 'lt': { + 'pt': __('Lituano'), + 'en': __('Lithuanian'), + 'es': __('Lituano') + }, + 'lv': { + 'pt': __('Letão'), + 'en': __('Latvian'), + 'es': __('Letón') + }, + 'et': { + 'pt': __('Estoniano'), + 'en': __('Estonian'), + 'es': __('Estonio') + }, + 'al': { # Alias (compatibilidade com ISO3166_ALPHA2 existente) + 'pt': __('Albanês'), + 'en': __('Albanian'), + 'es': __('Albanés') + }, + 'sq': { # Código ISO correto para albanês + 'pt': __('Albanês'), + 'en': __('Albanian'), + 'es': __('Albanés') + } +} + +# Mantido por compatibilidade - usar LANGUAGE_TRANSLATIONS para novos códigos ISO3166_ALPHA2 = { "pt": __("Português"), "en": __("Inglês"), diff --git a/opac/webapp/utils/utils.py b/opac/webapp/utils/utils.py old mode 100644 new mode 100755 index 830d614d8..1c005dd15 --- a/opac/webapp/utils/utils.py +++ b/opac/webapp/utils/utils.py @@ -24,6 +24,7 @@ from tenacity import (retry, retry_if_exception_type, stop_after_attempt, wait_exponential) from webapp import models +from webapp.choices import LANGUAGE_TRANSLATIONS from webapp.admin.forms import EmailForm from webapp.main.errors import internal_server_error, page_not_found from webapp.utils.page_migration import MigratedPage, PageMigration @@ -790,3 +791,86 @@ def fetch_and_extract_section(collection_acronym, journal_acronym, language): return extract_section(content, class_name) + + +# ============================================================================ +# Funções de Internacionalização de Idiomas +# ============================================================================ + +def get_language_name(lang_code, interface_lang='en'): + """ + Retorna o nome do idioma traduzido de acordo com o idioma da interface. + + Args: + lang_code (str): Código ISO 639-1 do idioma (ex: 'de', 'fr', 'ru') + interface_lang (str): Idioma da interface ('pt', 'en' ou 'es') + + Returns: + str: Nome do idioma traduzido ou código ISO em maiúsculo se não encontrado + + Exemplos: + >>> get_language_name('de', 'pt') + 'Alemão' + >>> get_language_name('de', 'en') + 'German' + >>> get_language_name('fr', 'es') + 'Francés' + >>> get_language_name('xx', 'pt') + 'XX' + """ + + if not lang_code: + return '' + + lang_code = lang_code.lower() + interface_lang = interface_lang.lower() + + # Se o código não está no dicionário, retorna em maiúsculo + if lang_code not in LANGUAGE_TRANSLATIONS: + return lang_code.upper() + + # Se o idioma da interface não está disponível, usa inglês como fallback + if interface_lang not in LANGUAGE_TRANSLATIONS[lang_code]: + interface_lang = 'en' + + # Retorna a tradução (lazy_gettext precisa ser convertido para string) + translation = LANGUAGE_TRANSLATIONS[lang_code].get(interface_lang, lang_code.upper()) + return str(translation) + + +def get_language_code_upper(lang_code): + """ + Retorna o código ISO do idioma em maiúsculo. + + Args: + lang_code (str): Código ISO 639-1 do idioma + + Returns: + str: Código em maiúsculo (ex: 'DE', 'FR', 'RU') + + Exemplos: + >>> get_language_code_upper('de') + 'DE' + >>> get_language_code_upper('fr') + 'FR' + """ + return lang_code.upper() if lang_code else '' + + +def register_language_filters(app): + """ + Registra os filtros Jinja2 para tradução de idiomas. + + Args: + app: Instância do Flask app + + Uso nos templates: + {{ 'de'|language_name('pt') }} -> 'Alemão' + {{ 'de'|language_code_upper }} -> 'DE' + """ + app.jinja_env.filters['language_name'] = get_language_name + app.jinja_env.filters['language_code_upper'] = get_language_code_upper + + # Também disponibiliza como função global nos templates + app.jinja_env.globals['get_language_name'] = get_language_name + app.jinja_env.globals['get_language_code_upper'] = get_language_code_upper From fe36ec3ad1500ff76c3ead6c5e772009bb9867b1 Mon Sep 17 00:00:00 2001 From: Rossi-Luciano Date: Sat, 10 Jan 2026 13:39:23 -0300 Subject: [PATCH 2/2] fix: traduz nomes de idiomas nos menus de artigos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Exibe nomes de idiomas traduzidos nos menus de resumo/texto/PDF - Mostra códigos ISO em maiúsculo no botão colapsado (DE, FR, RU) - Mostra nomes traduzidos no dropdown expandido (Alemão, German, Alemán) - Corrige IDs duplicados em levelMenu_pdf.html (btnGroupDropPDF) - Usa g.interface_language ao invés de session.get('lang') - Compatível com abordagem Accept-Language header do PR #366 Antes: Resumo (de), Texto (fr), PDF (ru) Depois: Resumo (Alemão), Texto (Francês), PDF (Russo) Corrige: TK-355 --- .../article/includes/levelMenu_abstracts.html | 36 ++++----------- .../article/includes/levelMenu_pdf.html | 45 ++++++------------- .../article/includes/levelMenu_texts.html | 39 ++++------------ 3 files changed, 29 insertions(+), 91 deletions(-) mode change 100644 => 100755 opac/webapp/templates/article/includes/levelMenu_abstracts.html mode change 100644 => 100755 opac/webapp/templates/article/includes/levelMenu_pdf.html mode change 100644 => 100755 opac/webapp/templates/article/includes/levelMenu_texts.html diff --git a/opac/webapp/templates/article/includes/levelMenu_abstracts.html b/opac/webapp/templates/article/includes/levelMenu_abstracts.html old mode 100644 new mode 100755 index cf4f1c8b1..60083f399 --- a/opac/webapp/templates/article/includes/levelMenu_abstracts.html +++ b/opac/webapp/templates/article/includes/levelMenu_abstracts.html @@ -5,15 +5,8 @@ {% trans %}Resumo{% endtrans %} {% for abstract in article.abstracts %} {% if gs_abstract and article_lang == abstract['language'] %} - {% if abstract['language'] == 'es' %} - (ES) - {% elif abstract['language'] == 'en' %} - (EN) - {% elif abstract['language'] == 'pt' %} - (PT) - {% else %} - {{ abstract['language'] }} - {% endif %} + {# Botão colapsado: mostra código ISO em MAIÚSCULO #} + ({{ abstract['language']|upper }}) {% endif %} {% endfor %} @@ -23,28 +16,16 @@ {% if gs_abstract and article_lang == abstract['language'] %} {% trans %}Resumo{% endtrans %} - {% if abstract['language'] == 'es' %} - ({% trans %}Espanhol{% endtrans %}) - {% elif abstract['language'] == 'en' %} - ({% trans %}Inglês{% endtrans %}) - {% elif abstract['language'] == 'pt' %} - ({% trans %}Português{% endtrans %}) - {% else %} - ({{ abstract['language'] }}) - {% endif %} + {# Dropdown expandido: mostra nome traduzido do idioma #} + {# g.interface_language contém o idioma da interface (pt/en/es) #} + {# Compatível com PR #366 - migração de session.get('lang') para g.interface_language #} + ({{ abstract['language']|language_name(g.interface_language) }}) {% else %} {% trans %}Resumo {% endtrans %} - {% if abstract['language'] == 'es' %} - ({% trans %}Espanhol{% endtrans %}) - {% elif abstract['language'] == 'en' %} - ({% trans %}Inglês{% endtrans %}) - {% elif abstract['language'] == 'pt' %} - ({% trans %}Português{% endtrans %}) - {% else %} - ({{ abstract['language'] }}) - {% endif %} + {# Dropdown expandido: mostra nome traduzido do idioma #} + ({{ abstract['language']|language_name(g.interface_language) }}) {% endif %}
  • @@ -52,4 +33,3 @@ {% endif %} - diff --git a/opac/webapp/templates/article/includes/levelMenu_pdf.html b/opac/webapp/templates/article/includes/levelMenu_pdf.html old mode 100644 new mode 100755 index 2f0e4b2ee..d1d46488f --- a/opac/webapp/templates/article/includes/levelMenu_pdf.html +++ b/opac/webapp/templates/article/includes/levelMenu_pdf.html @@ -1,22 +1,17 @@ {% if article and article.pdfs|length == 1 %}
    - -