import json
import os
import time
from pathlib import Path
import gradio as gr
from glossary_checker import GlossaryChecker
from llm_post_editor import LLMTranslationEditor
from trans_validator import TranslationValidator
# Configure paths
GLOSSARIES = {
"84000 Glossary": "data/84000_glossary.json",
}
def load_validate_and_edit(file_obj, selected_glossary, api_key, progress=gr.Progress()):
"""Process translations with progress updates."""
if not api_key or not api_key.startswith("sk-"):
return "Please provide a valid Anthropic API key (starts with 'sk-')"
try:
# Initialize progress tracking
progress(0, desc="Starting processing...")
# Read content from the file
content = file_obj.decode('utf-8')
progress(0.1, desc="File loaded")
# Save content to temporary file
temp_path = "temp_aligned.txt"
with open(temp_path, "w", encoding='utf-8') as f:
f.write(content)
# Count total lines for progress tracking
total_lines = len([line for line in content.split('\n') if line.strip()])
progress(0.15, desc=f"Found {total_lines} lines to process")
# Initialize components
progress(0.2, desc="Initializing validation...")
glossary_path = GLOSSARIES[selected_glossary]
checker = GlossaryChecker(glossary_path)
validator = TranslationValidator(checker, api_key)
# Run validation
progress(0.3, desc="Running validation...")
validation_results = validator.validate_translation(temp_path)
progress(0.6, desc="Validation complete")
# Initialize editor and get edited translations
progress(0.7, desc="Starting post-editing...")
editor = LLMTranslationEditor({"lines": validation_results}, api_key)
edited_translations = editor.post_edit_translations()
progress(0.9, desc="Post-editing complete")
# Create result display
progress(0.95, desc="Generating report...")
markdown_output = []
# Add summary
total_score = sum(r['score'] for r in validation_results) / len(validation_results)
markdown_output.append(f"# Validation Results\n")
markdown_output.append(f"**Overall Score**: {total_score:.2f}%\n")
markdown_output.append("*(Score based on terms counted in scoring)*\n\n")
markdown_output.append(f"**Total Lines**: {len(validation_results)}\n\n")
# Add processing statistics
modified_lines = sum(1 for t in edited_translations if t['modified'])
markdown_output.append("## Processing Statistics\n")
markdown_output.append(f"- Lines Modified: {modified_lines}/{len(validation_results)}\n")
markdown_output.append(f"- Processed at: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
# Add detailed results for each line
for idx, (validation, editing) in enumerate(zip(validation_results, edited_translations)):
markdown_output.append(f"## Line {validation['line_number']}\n")
markdown_output.append(f"**Score**: {validation['score']:.2f}%\n")
markdown_output.append(f"**Source**: {validation['source']}\n")
markdown_output.append(f"**Current Translation**: {validation['target']}\n")
# Add edited translation if available and modified
if editing['modified']:
markdown_output.append(f"\n**Post-Edited Translation**: {editing['edited']}\n")
markdown_output.append(f"\n**Editing Notes**: {editing['reasoning']}\n")
if validation['terms']:
# Separate terms into counted and not counted
counted_terms = []
other_terms = []
for term in validation['terms']:
if term['analysis']['translation_assessment']['should_be_counted']:
counted_terms.append(term)
else:
other_terms.append(term)
# Display counted terms in collapsible section
if counted_terms:
markdown_output.append("\n")
markdown_output.append("📊 Terms Counted in Scoring
\n")
for term in counted_terms:
analysis = term['analysis']
assessment = analysis['translation_assessment']
markdown_output.append(f"\n#### `{term['source_term']}` {'✅' if assessment['translated_correctly'] else '❌'}\n")
markdown_output.append(f"- Found Translation: **{analysis['translated_as']}**\n")
markdown_output.append(f"- Expected Translation: **{analysis['glossary_translation']}**\n")
# Add categories in collapsible section
markdown_output.append("\n")
markdown_output.append("Show Categories & Definitions
\n")
for cat_name in analysis['matching_categories']:
cat_data = term['categories'].get(cat_name, {})
markdown_output.append(f"\n*{cat_name}*:\n")
if 'translations' in cat_data:
markdown_output.append(f"- Translations: {', '.join(cat_data['translations'])}\n")
if 'definitions' in cat_data:
markdown_output.append(f"- Definitions: {', '.join(cat_data['definitions'])}\n")
markdown_output.append(" \n")
markdown_output.append(" \n")
# Display other terms in separate collapsible section
if other_terms:
markdown_output.append("\n")
markdown_output.append("Terms Not Counted in Scoring
\n")
for term in other_terms:
analysis = term['analysis']
markdown_output.append(f"\n#### `{term['source_term']}`\n")
markdown_output.append(f"- Found Translation: {analysis['translated_as']}\n")
markdown_output.append(f"- Note: Term not counted due to usage context\n")
# Add categories in collapsible section
markdown_output.append("\n")
markdown_output.append("Show Categories & Definitions
\n")
for cat_name in analysis['matching_categories']:
cat_data = term['categories'].get(cat_name, {})
markdown_output.append(f"\n*{cat_name}*:\n")
if 'translations' in cat_data:
markdown_output.append(f"- Translations: {', '.join(cat_data['translations'])}\n")
if 'definitions' in cat_data:
markdown_output.append(f"- Definitions: {', '.join(cat_data['definitions'])}\n")
markdown_output.append(" \n")
markdown_output.append(" \n")
markdown_output.append("\n---\n")
else:
markdown_output.append("\n*No glossary terms found in this line*\n\n---\n")
# Clean up temp file
os.remove(temp_path)
progress(1.0, desc="Processing complete!")
return "\n".join(markdown_output)
except Exception as e:
if os.path.exists(temp_path):
os.remove(temp_path)
return f"Error during processing: {str(e)}\n\nPlease check your input file and API key and try again."
# Create Gradio interface with examples
with gr.Blocks() as demo:
gr.Markdown("# Translation Validation & Editing Tool")
with gr.Row():
with gr.Column():
file_input = gr.File(
label="Upload aligned translations file (tab-separated)",
type="binary"
)
glossary_input = gr.Dropdown(
choices=list(GLOSSARIES.keys()),
label="Select Glossary",
value=list(GLOSSARIES.keys())[0]
)
api_key_input = gr.Textbox(
label="Anthropic API Key",
placeholder="sk-...",
type="password"
)
submit_btn = gr.Button("Process Translations", variant="primary")
# Add examples
gr.Examples(
examples=[
[str(Path("data/example_translations.txt").resolve()), "84000 Glossary", "sk-..."],
],
inputs=[file_input, glossary_input, api_key_input],
label="Example Inputs"
)
with gr.Column():
output = gr.Markdown()
gr.Markdown("""### Instructions
1. Upload a tab-separated file with Tibetan source and English translations
2. Select the glossary to use for validation
3. Enter your Anthropic API key
4. Click "Process Translations" and wait for results
The tool will:
- Validate translations against the glossary
- Calculate accuracy scores
- Suggest improvements using Claude
- Show detailed term analysis
Key:
- 📊 Terms used for scoring
- ✅ Correctly translated terms
- ❌ Terms needing improvement""")
submit_btn.click(
fn=load_validate_and_edit,
inputs=[file_input, glossary_input, api_key_input],
outputs=output
)
if __name__ == "__main__":
demo.launch()