Add system prompt, glossary, presets for Ollama/WebLLM, image translation support

This commit is contained in:
2025-11-30 16:45:41 +01:00
parent 465cab8a61
commit e48ea07e44
6 changed files with 497 additions and 51 deletions

View File

@@ -9,6 +9,8 @@ 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
import tempfile
import os
class PowerPointTranslator:
@@ -32,21 +34,23 @@ class PowerPointTranslator:
presentation = Presentation(input_path)
# Translate each slide
for slide in presentation.slides:
self._translate_slide(slide, target_language)
for slide_idx, slide in enumerate(presentation.slides):
self._translate_slide(slide, target_language, slide_idx + 1, input_path)
# Save the translated presentation
presentation.save(output_path)
return output_path
def _translate_slide(self, slide, target_language: str):
def _translate_slide(self, slide, target_language: str, slide_num: int, input_path: Path):
"""
Translate all text elements in a slide while preserving layout
Args:
slide: Slide to translate
target_language: Target language code
slide_num: Slide number for reference
input_path: Path to source file for image extraction
"""
# Translate notes (speaker notes)
if slide.has_notes_slide:
@@ -56,15 +60,16 @@ class PowerPointTranslator:
# Translate shapes in the slide
for shape in slide.shapes:
self._translate_shape(shape, target_language)
self._translate_shape(shape, target_language, slide)
def _translate_shape(self, shape: BaseShape, target_language: str):
def _translate_shape(self, shape: BaseShape, target_language: str, slide=None):
"""
Translate text in a shape based on its type
Args:
shape: Shape to translate
target_language: Target language code
slide: Parent slide for adding image translations
"""
# Handle text-containing shapes
if shape.has_text_frame:
@@ -74,20 +79,72 @@ class PowerPointTranslator:
if shape.shape_type == MSO_SHAPE_TYPE.TABLE:
self._translate_table(shape.table, target_language)
# Handle pictures/images
if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
self._translate_image_shape(shape, target_language, slide)
# 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)
self._translate_shape(sub_shape, target_language, slide)
# 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)
self._translate_shape(sub_shape, target_language, slide)
except:
pass # Some shapes may not support iteration
def _translate_image_shape(self, shape, target_language: str, slide):
"""
Translate text in an image using vision model and add as text box
"""
if not getattr(self.translation_service, 'translate_images', False):
return
from services.translation_service import OllamaTranslationProvider
if not isinstance(self.translation_service.provider, OllamaTranslationProvider):
return
try:
# Get image blob
image_blob = shape.image.blob
ext = shape.image.ext
# Save to temp file
with tempfile.NamedTemporaryFile(suffix=f'.{ext}', delete=False) as tmp:
tmp.write(image_blob)
tmp_path = tmp.name
# Translate with vision
translated_text = self.translation_service.provider.translate_image(tmp_path, target_language)
# Clean up
os.unlink(tmp_path)
if translated_text and translated_text.strip():
# Add text box below the image with translation
left = shape.left
top = shape.top + shape.height + Inches(0.1)
width = shape.width
height = Inches(0.5)
# Add text box
textbox = slide.shapes.add_textbox(left, top, width, height)
tf = textbox.text_frame
p = tf.paragraphs[0]
p.text = f"[{translated_text}]"
p.font.size = Pt(10)
p.font.italic = True
print(f"Added image translation: {translated_text[:50]}...")
except Exception as e:
print(f"Error translating image: {e}")
def _translate_text_frame(self, text_frame, target_language: str):
"""
Translate text within a text frame while preserving formatting