init: scripts diversos (crawlers, conversores, scrapers)

This commit is contained in:
2026-03-05 20:38:36 +00:00
commit 6ac6f4be2a
925 changed files with 850330 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
fix_malformed.py — Corrige ficheiros .po com msgstr mal formados.
Problemas detectados e corrigidos:
1. Aspa de fecho escapada (linha termina com \")
2. Sequência de escape inválida (backslash + espaço)
3. Aspas internas não escapadas
Se existirem linhas de continuação, usa-as como tradução correcta.
Se não houver continuação, fecha a aspa em falta.
Uso:
python3 fix_malformed.py plugin-pt_PT.po [plugin2-pt_PT.po ...]
find /path/to/library -name "*-pt_PT.po" | xargs python3 fix_malformed.py
Author: Descomplicar®
Version: 1.1.0
"""
import sys
import re
import shutil
import subprocess
from pathlib import Path
def last_quote_is_escaped(s: str) -> bool:
"""Verifica se a última aspas está escapada com backslash."""
if not s.endswith('"'):
return False
count = 0
i = len(s) - 2
while i >= 0 and s[i] == '\\':
count += 1
i -= 1
return count % 2 == 1
def has_invalid_escape(s: str) -> bool:
"""Detecta sequências de escape inválidas (backslash + espaço/tab)."""
return bool(re.search(r'\\[ \t]', s))
def has_unescaped_internal_quote(s: str) -> bool:
"""Detecta aspas não escapadas no interior da string."""
i = 0
while i < len(s):
if s[i] == '\\':
i += 2
continue
if s[i] == '"':
return True
i += 1
return False
def is_malformed(line: str) -> bool:
"""Determina se uma linha msgstr está mal formada."""
if not line.startswith('msgstr "'):
return False
content = line[7:]
# Vazio é válido
if content == '""':
return False
# Não termina em aspas, ou a aspas final está escapada
if not content.endswith('"') or last_quote_is_escaped(content):
return True
# Escape inválido no interior
if has_invalid_escape(content):
return True
# Aspas internas não escapadas
inner = content[1:-1]
if has_unescaped_internal_quote(inner):
return True
return False
def process_file(filepath: str) -> tuple[int, bool]:
"""
Processa um ficheiro .po e corrige malformações.
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 = []
i = 0
fixes = 0
while i < len(lines):
line = lines[i].rstrip('\n')
if is_malformed(line):
# Verificar se há linhas de continuação
continuation = []
j = i + 1
while j < len(lines) and lines[j].rstrip('\n').startswith('"'):
continuation.append(lines[j])
j += 1
if continuation:
# Usar continuação como tradução correcta
new_lines.append('msgstr ""\n')
for c in continuation:
new_lines.append(c)
fixes += 1
i = j
continue
elif not line.endswith('"') or last_quote_is_escaped(line):
# Fechar aspa em falta
new_lines.append(line + '"\n')
fixes += 1
i += 1
continue
new_lines.append(lines[i])
i += 1
if fixes > 0:
shutil.copy2(filepath, filepath + '.bak_malformed')
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) | .mo: {mo_status}")
return fixes, mo_ok
else:
print(f" {path.name}: sem malformações")
return 0, True
def main():
if len(sys.argv) < 2:
print("Uso: python3 fix_malformed.py <ficheiro.po> [...]")
print(" find /path -name '*-pt_PT.po' | xargs python3 fix_malformed.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ções em {total_files} ficheiro(s) | Erros .mo: {errors}")
if __name__ == '__main__':
main()