HH/translate_templates.py
2025-12-29 11:52:54 +03:00

192 lines
6.9 KiB
Python

#!/usr/bin/env python3
"""
Comprehensive script to add i18n translation tags to all Django templates
"""
import os
import re
from pathlib import Path
# Patterns for text that should be translated
TRANSLATION_PATTERNS = [
# Simple text in common HTML elements
(r'>([A-Z][^<>{}\n]+?)</h[1-6]>', r'>{%% trans "%s" %%}</h'),
(r'>([A-Z][^<>{}\n]+?)</button>', r'>{%% trans "%s" %%}</button>'),
(r'>([A-Z][^<>{}\n]+?)</label>', r'>{%% trans "%s" %%}</label>'),
(r'>([A-Z][^<>{}\n]+?)</th>', r'>{%% trans "%s" %%}</th>'),
(r'>([A-Z][^<>{}\n]+?)</a>', r'>{%% trans "%s" %%}</a>'),
(r'>([A-Z][^<>{}\n]+?)</span>', r'>{%% trans "%s" %%}</span>'),
(r'>([A-Z][^<>{}\n]+?)</p>', r'>{%% trans "%s" %%}</p>'),
(r'>([A-Z][^<>{}\n]+?)</div>', r'>{%% trans "%s" %%}</div>'),
(r'>([A-Z][^<>{}\n]+?)</li>', r'>{%% trans "%s" %%}</li>'),
(r'>([A-Z][^<>{}\n]+?)</td>', r'>{%% trans "%s" %%}</td>'),
(r'>([A-Z][^<>{}\n]+?)</option>', r'>{%% trans "%s" %%}</option>'),
]
def add_i18n_load(content):
"""Add {% load i18n %} at the top if not present"""
if '{% load i18n %}' in content or '{%load i18n%}' in content:
return content
# Check if it extends a template
if content.strip().startswith('{% extends'):
# Add after extends
lines = content.split('\n')
for i, line in enumerate(lines):
if '{% extends' in line:
lines.insert(i + 1, '{% load i18n %}')
return '\n'.join(lines)
# Add at the very top
return '{% load i18n %}\n' + content
def wrap_text_in_trans(text):
"""Wrap text in {% trans %} tag"""
text = text.strip()
if not text or '{%' in text or '{{' in text:
return text
return f'{{% trans "{text}" %}}'
def process_template_content(content):
"""Process template content and add translation tags"""
lines = content.split('\n')
processed_lines = []
for line in lines:
original_line = line
# Skip lines that already have trans tags
if '{% trans' in line or '{%trans' in line:
processed_lines.append(line)
continue
# Skip lines with only Django template tags or variables
if line.strip().startswith('{%') or line.strip().startswith('{{'):
processed_lines.append(line)
continue
# Process specific patterns
# Button text
if '<button' in line and '</button>' in line:
match = re.search(r'>([^<>{}\n]+)</button>', line)
if match:
text = match.group(1).strip()
if text and not '{' in text and len(text) > 1:
line = line.replace(f'>{text}</button>', f'>{{% trans "{text}" %}}</button>')
# Link text
if '<a ' in line and '</a>' in line:
match = re.search(r'>([^<>{}\n]+)</a>', line)
if match:
text = match.group(1).strip()
if text and not '{' in text and len(text) > 1 and not text.startswith('<'):
line = line.replace(f'>{text}</a>', f'>{{% trans "{text}" %}}</a>')
# Label text
if '<label' in line and '</label>' in line:
match = re.search(r'>([^<>{}\n]+)</label>', line)
if match:
text = match.group(1).strip()
if text and not '{' in text and len(text) > 1:
line = line.replace(f'>{text}</label>', f'>{{% trans "{text}" %}}</label>')
# Heading text
for i in range(1, 7):
if f'<h{i}' in line and f'</h{i}>' in line:
match = re.search(rf'>([^<>{{}}\n]+)</h{i}>', line)
if match:
text = match.group(1).strip()
if text and not '{' in text and len(text) > 1:
line = line.replace(f'>{text}</h{i}>', f'>{{% trans "{text}" %}}</h{i}>')
# Table headers
if '<th' in line and '</th>' in line:
match = re.search(r'>([^<>{}\n]+)</th>', line)
if match:
text = match.group(1).strip()
if text and not '{' in text and len(text) > 1:
line = line.replace(f'>{text}</th>', f'>{{% trans "{text}" %}}</th>')
# Paragraph text (be careful with this one)
if '<p' in line and '</p>' in line and '<p>' in line:
match = re.search(r'<p>([^<>{}\n]+)</p>', line)
if match:
text = match.group(1).strip()
if text and not '{' in text and len(text) > 1:
line = line.replace(f'<p>{text}</p>', f'<p>{{% trans "{text}" %}}</p>')
# Placeholder attributes
if 'placeholder="' in line:
match = re.search(r'placeholder="([^"]+)"', line)
if match:
text = match.group(1)
if not '{' in text:
line = line.replace(f'placeholder="{text}"', f'placeholder="{{% trans \'{text}\' %}}"')
# Title attributes
if 'title="' in line and not '{% trans' in line:
match = re.search(r'title="([^"]+)"', line)
if match:
text = match.group(1)
if not '{' in text and len(text) > 2:
line = line.replace(f'title="{text}"', f'title="{{% trans \'{text}\' %}}"')
processed_lines.append(line)
return '\n'.join(processed_lines)
def process_template(filepath):
"""Process a single template file"""
print(f"Processing: {filepath}")
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
original_content = content
# Add i18n load tag
content = add_i18n_load(content)
# Process content for translations
content = process_template_content(content)
if content != original_content:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
print(f" ✓ Updated: {filepath}")
return True
else:
print(f" - No changes needed: {filepath}")
return False
except Exception as e:
print(f" ✗ Error processing {filepath}: {e}")
return False
def main():
"""Main function to process all templates"""
base_dir = Path(__file__).parent
templates_dir = base_dir / 'templates'
if not templates_dir.exists():
print(f"Templates directory not found: {templates_dir}")
return
# Find all HTML files
html_files = sorted(list(templates_dir.rglob('*.html')))
print(f"Found {len(html_files)} template files")
print("=" * 80)
updated_count = 0
for html_file in html_files:
if process_template(html_file):
updated_count += 1
print()
print("=" * 80)
print(f"Completed! Updated {updated_count} out of {len(html_files)} files")
if __name__ == '__main__':
main()