349 lines
12 KiB
Python
Executable File
349 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
fix_ptbr.py — Corrige termos PT-BR para PT-PT em ficheiros .po.
|
|
|
|
Aplica substituições apenas em linhas msgstr (nunca em msgid ou comentários).
|
|
Preserva placeholders e marcas registadas.
|
|
|
|
Uso:
|
|
python3 fix_ptbr.py plugin-pt_PT.po [plugin2-pt_PT.po ...]
|
|
find /path/to/library -name "*-pt_PT.po" | xargs python3 fix_ptbr.py
|
|
|
|
Author: Descomplicar®
|
|
Version: 1.1.0
|
|
"""
|
|
import sys
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
# Substituições PT-BR → PT-PT (padrão: word boundary para evitar falsos positivos)
|
|
FIXES = [
|
|
# --- Verbos ---
|
|
(r'\bsalvar\b', 'guardar'),
|
|
(r'\bSalvar\b', 'Guardar'),
|
|
(r'\bsalve\b', 'guarde'),
|
|
(r'\bSalve\b', 'Guarde'),
|
|
(r'\bativar\b', 'activar'),
|
|
(r'\bAtivar\b', 'Activar'),
|
|
(r'\bativado\b', 'activado'),
|
|
(r'\bAtivado\b', 'Activado'),
|
|
(r'\bativados\b', 'activados'),
|
|
(r'\bAtivados\b', 'Activados'),
|
|
(r'\bativada\b', 'activada'),
|
|
(r'\bAtivada\b', 'Activada'),
|
|
(r'\batualizar\b', 'actualizar'),
|
|
(r'\bAtualizar\b', 'Actualizar'),
|
|
(r'\batualização\b', 'actualização'),
|
|
(r'\bAtualização\b', 'Actualização'),
|
|
(r'\batualizações\b', 'actualizações'),
|
|
(r'\bAtualizações\b', 'Actualizações'),
|
|
(r'\bdesativar\b', 'desactivar'),
|
|
(r'\bDesativar\b', 'Desactivar'),
|
|
(r'\bdesativado\b', 'desactivado'),
|
|
(r'\bDesativado\b', 'Desactivado'),
|
|
(r'\bdesativada\b', 'desactivada'),
|
|
(r'\bDesativada\b', 'Desactivada'),
|
|
(r'\bconectar\b', 'ligar'),
|
|
(r'\bConectar\b', 'Ligar'),
|
|
(r'\bconectado\b', 'ligado'),
|
|
(r'\bConectado\b', 'Ligado'),
|
|
(r'\bdesconectar\b', 'desligar'),
|
|
(r'\bDesconectar\b', 'Desligar'),
|
|
(r'\bdesconectado\b', 'desligado'),
|
|
(r'\bDesconectado\b', 'Desligado'),
|
|
(r'\breconectar\b', 'religar'),
|
|
(r'\bReconectar\b', 'Religar'),
|
|
(r'\bselecionar\b', 'seleccionar'),
|
|
(r'\bSelecionar\b', 'Seleccionar'),
|
|
(r'\bexcluir\b', 'eliminar'),
|
|
(r'\bExcluir\b', 'Eliminar'),
|
|
(r'\bexcluído\b', 'eliminado'),
|
|
(r'\bExcluído\b', 'Eliminado'),
|
|
(r'\bexcluída\b', 'eliminada'),
|
|
(r'\bExcluída\b', 'Eliminada'),
|
|
(r'\bexcluídos\b', 'eliminados'),
|
|
(r'\bExcluídos\b', 'Eliminados'),
|
|
(r'\bexcluídas\b', 'eliminadas'),
|
|
(r'\bExcluídas\b', 'Eliminadas'),
|
|
(r'\bacessar\b', 'aceder'),
|
|
(r'\bAcessar\b', 'Aceder'),
|
|
(r'\bcompartilhar\b', 'partilhar'),
|
|
(r'\bCompartilhar\b', 'Partilhar'),
|
|
(r'\bcompartilhado\b', 'partilhado'),
|
|
(r'\bCompartilhado\b', 'Partilhado'),
|
|
(r'\bcompartilhada\b', 'partilhada'),
|
|
(r'\bCompartilhada\b', 'Partilhada'),
|
|
(r'\bcompartilhados\b', 'partilhados'),
|
|
(r'\bCompartilhados\b', 'Partilhados'),
|
|
(r'\bcompartilhadas\b', 'partilhadas'),
|
|
(r'\bCompartilhadas\b', 'Partilhadas'),
|
|
(r'\bdigite\b', 'introduza'),
|
|
(r'\bDigite\b', 'Introduza'),
|
|
# --- Substantivos ---
|
|
(r'\bsenha\b', 'palavra-passe'),
|
|
(r'\bSenha\b', 'Palavra-passe'),
|
|
(r'\bsenhas\b', 'palavras-passe'),
|
|
(r'\bSenhas\b', 'Palavras-passe'),
|
|
(r'\busuário\b', 'utilizador'),
|
|
(r'\bUsuário\b', 'Utilizador'),
|
|
(r'\busuários\b', 'utilizadores'),
|
|
(r'\bUsuários\b', 'Utilizadores'),
|
|
(r'\busuario\b', 'utilizador'),
|
|
(r'\bUsuario\b', 'Utilizador'),
|
|
(r'\barquivo\b', 'ficheiro'),
|
|
(r'\bArquivo\b', 'Ficheiro'),
|
|
(r'\barquivos\b', 'ficheiros'),
|
|
(r'\bArquivos\b', 'Ficheiros'),
|
|
(r'\bconexão\b', 'ligação'),
|
|
(r'\bConexão\b', 'Ligação'),
|
|
(r'\bseleção\b', 'selecção'),
|
|
(r'\bSeleção\b', 'Selecção'),
|
|
(r'\bselecionado\b', 'seleccionado'),
|
|
(r'\bSelecionado\b', 'Seleccionado'),
|
|
(r'\baplicativo\b', 'aplicação'),
|
|
(r'\bAplicativo\b', 'Aplicação'),
|
|
(r'\baplicativos\b', 'aplicações'),
|
|
(r'\bAplicativos\b', 'Aplicações'),
|
|
(r'\bdashboard\b', 'painel'),
|
|
(r'\bDashboard\b', 'Painel'),
|
|
(r'\bbackup\b', 'cópia de segurança'),
|
|
(r'\bBackup\b', 'Cópia de segurança'),
|
|
(r'\bbackups\b', 'cópias de segurança'),
|
|
(r'\bBackups\b', 'Cópias de segurança'),
|
|
(r'\bpostagem\b', 'publicação'),
|
|
(r'\bPostagem\b', 'Publicação'),
|
|
(r'\bpostagens\b', 'publicações'),
|
|
(r'\bPostagens\b', 'Publicações'),
|
|
(r'\bativo\b', 'activo'),
|
|
(r'\bAtivo\b', 'Activo'),
|
|
(r'\bativos\b', 'activos'),
|
|
(r'\bAtivos\b', 'Activos'),
|
|
(r'\bativa\b', 'activa'),
|
|
(r'\bAtiva\b', 'Activa'),
|
|
(r'\bativas\b', 'activas'),
|
|
(r'\bAtivas\b', 'Activas'),
|
|
(r'\binativo\b', 'inactivo'),
|
|
(r'\bInativo\b', 'Inactivo'),
|
|
(r'\binativos\b', 'inactivos'),
|
|
(r'\bInativos\b', 'Inactivos'),
|
|
(r'\binativa\b', 'inactiva'),
|
|
(r'\bInativa\b', 'Inactiva'),
|
|
(r'\binativas\b', 'inactivas'),
|
|
(r'\bInativas\b', 'Inactivas'),
|
|
(r'\bativação\b', 'activação'),
|
|
(r'\bAtivação\b', 'Activação'),
|
|
(r'\bdesativação\b', 'desactivação'),
|
|
(r'\bDesativação\b', 'Desactivação'),
|
|
# --- Ortografia PT-PT ---
|
|
# Formas garbled LibreTranslate (Updated→Atualizard, Updates→Atualizars)
|
|
(r'\bAtualizard\b', 'Actualizado'),
|
|
(r'\batualizard\b', 'actualizado'),
|
|
(r'\bAtualizars\b', 'Actualizações'),
|
|
(r'\batualizars\b', 'actualizações'),
|
|
# Formas maiúsculas (all-caps)
|
|
(r'\bATUALIZAR\b', 'ACTUALIZAR'),
|
|
(r'\bATUALIZAÇÃO\b', 'ACTUALIZAÇÃO'),
|
|
(r'\bATIVAR\b', 'ACTIVAR'),
|
|
(r'\bDESATIVAR\b', 'DESACTIVAR'),
|
|
(r'\bATIVADO\b', 'ACTIVADO'),
|
|
(r'\bDESATIVADO\b', 'DESACTIVADO'),
|
|
(r'\bARQUIVO\b', 'FICHEIRO'),
|
|
(r'\bARQUIVOS\b', 'FICHEIROS'),
|
|
# Todas as formas de atualiz* → actualiz*
|
|
(r'\batualizada\b', 'actualizada'),
|
|
(r'\bAtualizada\b', 'Actualizada'),
|
|
(r'\batualizadas\b', 'actualizadas'),
|
|
(r'\bAtualizadas\b', 'Actualizadas'),
|
|
(r'\batualizado\b', 'actualizado'),
|
|
(r'\bAtualizado\b', 'Actualizado'),
|
|
(r'\batualizados\b', 'actualizados'),
|
|
(r'\bAtualizados\b', 'Actualizados'),
|
|
(r'\batualize\b', 'actualize'),
|
|
(r'\bAtualize\b', 'Actualize'),
|
|
(r'\batualiza\b', 'actualiza'),
|
|
(r'\bAtualiza\b', 'Actualiza'),
|
|
(r'\batualizando\b', 'actualizando'),
|
|
(r'\bAtualizando\b', 'Actualizando'),
|
|
(r'\batualizou\b', 'actualizou'),
|
|
(r'\bAtualizou\b', 'Actualizou'),
|
|
(r'\batualizem\b', 'actualizem'),
|
|
(r'\bAtualizem\b', 'Actualizem'),
|
|
(r'\batualizam\b', 'actualizam'),
|
|
(r'\bAtualizam\b', 'Actualizam'),
|
|
(r'\batualizamos\b', 'actualizamos'),
|
|
(r'\bAtualizamos\b', 'Actualizamos'),
|
|
(r'\batualizei\b', 'actualizei'),
|
|
(r'\bAtualizei\b', 'Actualizei'),
|
|
(r'\batualizará\b', 'actualizará'),
|
|
(r'\bAtualizará\b', 'Actualizará'),
|
|
(r'\batualizarão\b', 'actualizarão'),
|
|
(r'\bAtualizarão\b', 'Actualizarão'),
|
|
(r'\batualizarei\b', 'actualizarei'),
|
|
(r'\bAtualizarei\b', 'Actualizarei'),
|
|
(r'\batualizaremos\b', 'actualizaremos'),
|
|
(r'\bAtualizaremos\b', 'Actualizaremos'),
|
|
(r'\batualizarás\b', 'actualizarás'),
|
|
(r'\batualizassem\b', 'actualizassem'),
|
|
# Formas com acento (atualizá-los, atualizá-la)
|
|
(r'\batualizá\b', 'actualizá'),
|
|
(r'\bAtualizá\b', 'Actualizá'),
|
|
# Habilitar/desabilitar → activar/desactivar
|
|
(r'\bhabilitado\b', 'activado'),
|
|
(r'\bHabilitado\b', 'Activado'),
|
|
(r'\bhabilitada\b', 'activada'),
|
|
(r'\bHabilitada\b', 'Activada'),
|
|
(r'\bhabilitados\b', 'activados'),
|
|
(r'\bHabilitados\b', 'Activados'),
|
|
(r'\bhabilitadas\b', 'activadas'),
|
|
(r'\bHabilitadas\b', 'Activadas'),
|
|
(r'\bhabilitar\b', 'activar'),
|
|
(r'\bHabilitar\b', 'Activar'),
|
|
(r'\bdesabilitado\b', 'desactivado'),
|
|
(r'\bDesabilitado\b', 'Desactivado'),
|
|
(r'\bdesabilitada\b', 'desactivada'),
|
|
(r'\bDesabilitada\b', 'Desactivada'),
|
|
(r'\bdesabilitados\b', 'desactivados'),
|
|
(r'\bDesabilitados\b', 'Desactivados'),
|
|
(r'\bdesabilitar\b', 'desactivar'),
|
|
(r'\bDesabilitar\b', 'Desactivar'),
|
|
(r'\bdesabilitadas\b', 'desactivadas'),
|
|
(r'\bDesabilitadas\b', 'Desactivadas'),
|
|
# Formas plurais de desativar
|
|
(r'\bdesativados\b', 'desactivados'),
|
|
(r'\bDesativados\b', 'Desactivados'),
|
|
(r'\bdesativadas\b', 'desactivadas'),
|
|
(r'\bDesativadas\b', 'Desactivadas'),
|
|
(r'\bdesativando\b', 'desactivando'),
|
|
(r'\bDesativando\b', 'Desactivando'),
|
|
# Formas plurais de ativar
|
|
(r'\bativados\b', 'activados'),
|
|
(r'\bAtivados\b', 'Activados'),
|
|
(r'\bativadas\b', 'activadas'),
|
|
(r'\bAtivadas\b', 'Activadas'),
|
|
(r'\bativando\b', 'activando'),
|
|
(r'\bAtivando\b', 'Activando'),
|
|
(r'\bfatura\b', 'factura'),
|
|
(r'\bFatura\b', 'Factura'),
|
|
(r'\bfaturas\b', 'facturas'),
|
|
(r'\bFaturas\b', 'Facturas'),
|
|
(r'\bfacturar\b', 'facturar'),
|
|
(r'\bóptico\b', 'óptico'),
|
|
(r'\bótimo\b', 'óptimo'),
|
|
(r'\bÓtimo\b', 'Óptimo'),
|
|
(r'\bação\b', 'acção'),
|
|
(r'\bAção\b', 'Acção'),
|
|
(r'\bações\b', 'acções'),
|
|
(r'\bAções\b', 'Acções'),
|
|
(r'\bdireito\b', 'direito'), # igual — sem alteração necessária
|
|
(r'\bdiretamente\b', 'directamente'),
|
|
(r'\bDiretamente\b', 'Directamente'),
|
|
(r'\bdireto\b', 'directo'),
|
|
(r'\bDireto\b', 'Directo'),
|
|
(r'\bcontacto\b', 'contacto'), # já correcto
|
|
# --- Pronomes PT-BR ---
|
|
(r'\bVOCÊ\b', 'SI'),
|
|
(r'\bVOCÊS\b', 'VÓS'),
|
|
(r'\bvocê\b', 'o utilizador'),
|
|
(r'\bVocê\b', 'O utilizador'),
|
|
(r'\bvocês\b', 'os utilizadores'),
|
|
(r'\bVocês\b', 'Os utilizadores'),
|
|
# --- Expressões compostas ---
|
|
(r'\bsua conta\b', 'a sua conta'),
|
|
(r'\bSua conta\b', 'A sua conta'),
|
|
(r'\bnome de usuário\b', 'nome de utilizador'),
|
|
(r'\bNome de usuário\b', 'Nome de utilizador'),
|
|
(r'\bnome de usuario\b', 'nome de utilizador'),
|
|
]
|
|
|
|
|
|
def fix_line(line: str) -> str:
|
|
"""Aplica todas as substituições PT-BR → PT-PT numa linha."""
|
|
result = line
|
|
for pattern, replacement in FIXES:
|
|
result = re.sub(pattern, replacement, result)
|
|
return result
|
|
|
|
|
|
def process_file(filepath: str) -> tuple[int, bool]:
|
|
"""
|
|
Processa um ficheiro .po e corrige PT-BR → PT-PT.
|
|
Devolve (número de correcções, sucesso do .mo).
|
|
"""
|
|
path = Path(filepath)
|
|
if not path.exists():
|
|
print(f" ERRO: ficheiro não encontrado: {filepath}", file=sys.stderr)
|
|
return 0, False
|
|
|
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
lines = f.readlines()
|
|
|
|
new_lines = []
|
|
in_msgstr = False
|
|
fixes = 0
|
|
|
|
for line in lines:
|
|
stripped = line.rstrip('\n')
|
|
|
|
if stripped.startswith('msgstr ') or re.match(r'^msgstr\[\d+\]', stripped):
|
|
in_msgstr = True
|
|
fixed = fix_line(stripped)
|
|
if fixed != stripped:
|
|
fixes += 1
|
|
new_lines.append(fixed + '\n')
|
|
elif stripped.startswith('"') and in_msgstr:
|
|
fixed = fix_line(stripped)
|
|
if fixed != stripped:
|
|
fixes += 1
|
|
new_lines.append(fixed + '\n')
|
|
else:
|
|
if not stripped or stripped.startswith('#') or \
|
|
stripped.startswith('msgid') or stripped.startswith('msgctxt'):
|
|
in_msgstr = False
|
|
new_lines.append(line)
|
|
|
|
if fixes > 0:
|
|
shutil.copy2(filepath, filepath + '.bak_ptbr')
|
|
with open(filepath, 'w', encoding='utf-8') as f:
|
|
f.writelines(new_lines)
|
|
|
|
mo_path = filepath.replace('.po', '.mo')
|
|
r = subprocess.run(
|
|
['msgfmt', filepath, '-o', mo_path],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
)
|
|
mo_ok = r.returncode == 0
|
|
mo_status = "OK" if mo_ok else f"ERRO: {r.stderr.decode()[:80]}"
|
|
print(f" {path.name}: {fixes} correcção(ões) PT-BR | .mo: {mo_status}")
|
|
return fixes, mo_ok
|
|
else:
|
|
print(f" {path.name}: sem PT-BR detectado")
|
|
return 0, True
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Uso: python3 fix_ptbr.py <ficheiro.po> [...]")
|
|
print(" find /path -name '*-pt_PT.po' | xargs python3 fix_ptbr.py")
|
|
sys.exit(1)
|
|
|
|
total_fixes = 0
|
|
total_files = 0
|
|
errors = 0
|
|
|
|
for filepath in sys.argv[1:]:
|
|
total_files += 1
|
|
fixes, ok = process_file(filepath)
|
|
total_fixes += fixes
|
|
if not ok:
|
|
errors += 1
|
|
|
|
print(f"\nTotal: {total_fixes} correcção(ões) em {total_files} ficheiro(s) | Erros .mo: {errors}")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|