159 lines
5.3 KiB
Python
159 lines
5.3 KiB
Python
"""
|
|
PowerPoint Translation Module
|
|
Translates PowerPoint files while preserving all layouts, animations, and media
|
|
"""
|
|
from pathlib import Path
|
|
from pptx import Presentation
|
|
from pptx.shapes.base import BaseShape
|
|
from pptx.shapes.group import GroupShape
|
|
from pptx.util import Inches, Pt
|
|
from pptx.enum.shapes import MSO_SHAPE_TYPE
|
|
from services.translation_service import translation_service
|
|
|
|
|
|
class PowerPointTranslator:
|
|
"""Handles translation of PowerPoint presentations with strict formatting preservation"""
|
|
|
|
def __init__(self):
|
|
self.translation_service = translation_service
|
|
|
|
def translate_file(self, input_path: Path, output_path: Path, target_language: str) -> Path:
|
|
"""
|
|
Translate a PowerPoint presentation while preserving all formatting and structure
|
|
|
|
Args:
|
|
input_path: Path to input PowerPoint file
|
|
output_path: Path to save translated PowerPoint file
|
|
target_language: Target language code
|
|
|
|
Returns:
|
|
Path to the translated file
|
|
"""
|
|
presentation = Presentation(input_path)
|
|
|
|
# Translate each slide
|
|
for slide in presentation.slides:
|
|
self._translate_slide(slide, target_language)
|
|
|
|
# Save the translated presentation
|
|
presentation.save(output_path)
|
|
|
|
return output_path
|
|
|
|
def _translate_slide(self, slide, target_language: str):
|
|
"""
|
|
Translate all text elements in a slide while preserving layout
|
|
|
|
Args:
|
|
slide: Slide to translate
|
|
target_language: Target language code
|
|
"""
|
|
# Translate notes (speaker notes)
|
|
if slide.has_notes_slide:
|
|
notes_slide = slide.notes_slide
|
|
if notes_slide.notes_text_frame:
|
|
self._translate_text_frame(notes_slide.notes_text_frame, target_language)
|
|
|
|
# Translate shapes in the slide
|
|
for shape in slide.shapes:
|
|
self._translate_shape(shape, target_language)
|
|
|
|
def _translate_shape(self, shape: BaseShape, target_language: str):
|
|
"""
|
|
Translate text in a shape based on its type
|
|
|
|
Args:
|
|
shape: Shape to translate
|
|
target_language: Target language code
|
|
"""
|
|
# Handle text-containing shapes
|
|
if shape.has_text_frame:
|
|
self._translate_text_frame(shape.text_frame, target_language)
|
|
|
|
# Handle tables
|
|
if shape.shape_type == MSO_SHAPE_TYPE.TABLE:
|
|
self._translate_table(shape.table, target_language)
|
|
|
|
# Handle group shapes (shapes within shapes)
|
|
if shape.shape_type == MSO_SHAPE_TYPE.GROUP:
|
|
for sub_shape in shape.shapes:
|
|
self._translate_shape(sub_shape, target_language)
|
|
|
|
# Handle smart art (contains multiple shapes)
|
|
# Smart art is complex, but we can try to translate text within it
|
|
if hasattr(shape, 'shapes'):
|
|
try:
|
|
for sub_shape in shape.shapes:
|
|
self._translate_shape(sub_shape, target_language)
|
|
except:
|
|
pass # Some shapes may not support iteration
|
|
|
|
def _translate_text_frame(self, text_frame, target_language: str):
|
|
"""
|
|
Translate text within a text frame while preserving formatting
|
|
|
|
Args:
|
|
text_frame: Text frame to translate
|
|
target_language: Target language code
|
|
"""
|
|
if not text_frame.text.strip():
|
|
return
|
|
|
|
# Translate each paragraph in the text frame
|
|
for paragraph in text_frame.paragraphs:
|
|
self._translate_paragraph(paragraph, target_language)
|
|
|
|
def _translate_paragraph(self, paragraph, target_language: str):
|
|
"""
|
|
Translate a paragraph while preserving run-level formatting
|
|
|
|
Args:
|
|
paragraph: Paragraph to translate
|
|
target_language: Target language code
|
|
"""
|
|
if not paragraph.text.strip():
|
|
return
|
|
|
|
# Translate each run in the paragraph to preserve individual formatting
|
|
for run in paragraph.runs:
|
|
if run.text.strip():
|
|
translated_text = self.translation_service.translate_text(
|
|
run.text, target_language
|
|
)
|
|
run.text = translated_text
|
|
|
|
def _translate_table(self, table, target_language: str):
|
|
"""
|
|
Translate all cells in a table while preserving structure
|
|
|
|
Args:
|
|
table: Table to translate
|
|
target_language: Target language code
|
|
"""
|
|
for row in table.rows:
|
|
for cell in row.cells:
|
|
self._translate_text_frame(cell.text_frame, target_language)
|
|
|
|
def _is_translatable(self, text: str) -> bool:
|
|
"""
|
|
Determine if text should be translated
|
|
|
|
Args:
|
|
text: Text to check
|
|
|
|
Returns:
|
|
True if text should be translated, False otherwise
|
|
"""
|
|
if not text or not isinstance(text, str):
|
|
return False
|
|
|
|
# Don't translate if it's only numbers, special characters, or very short
|
|
if len(text.strip()) < 2:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
# Global translator instance
|
|
pptx_translator = PowerPointTranslator()
|