fix(direction): le sens du document traduit suit la langue cible, dans les deux sens
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3m3s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3m3s
Persan -> francais : le document traduit restait en lecture de droite a gauche, heritee de la source. Trois causes : - PDF : regression du renommage precedent — le choix d'alignement testait la fonction is_rtl (toujours vraie) au lieu du parametre : tout texte traduit s'alignait a droite, quelle que soit la cible. Corrige + test epinglant l'alignement gauche pour une cible latine. - Word : les marques RTL heritees (bidi, rtl, bidiVisual, notes et commentaires compris) sont desormais retirees quand la cible est latine ; retrait fait sur une liste figee (l'iterateur lxml sautait des elements pendant la suppression). - Excel : les feuilles heritees d'un affichage droite-a-gauche sont remises en lecture gauche-a-droite pour une cible latine. - PowerPoint : les attributs rtl herites sont retires pour une cible latine (alignements visuels conserves). 3 tests de bout en bout nouveaux : document RTL traduit vers le francais ressort en lecture gauche-a-droite dans les trois formats.
This commit is contained in:
@@ -855,6 +855,21 @@ class TestPdfLayoutModeRtl:
|
||||
assert ok is True
|
||||
assert page.calls[0]["text"] == "Bonjour le monde"
|
||||
|
||||
def test_ltr_target_is_left_aligned(self):
|
||||
"""Regression guard: a Latin target (e.g. Persian → French) must be
|
||||
left-aligned. A previous rename once made the alignment test the
|
||||
truthiness of the imported is_rtl *function* — always true — and
|
||||
every translated PDF came out right-aligned."""
|
||||
import fitz
|
||||
|
||||
translator = PDFTranslator()
|
||||
page = _FakePage()
|
||||
ok = translator._write_translated_block(
|
||||
page, self._block("Bonjour le monde"), font_path=None, rtl_target=False
|
||||
)
|
||||
assert ok is True
|
||||
assert page.calls[0]["align"] == fitz.TEXT_ALIGN_LEFT
|
||||
|
||||
|
||||
class TestPdfFontSelection:
|
||||
def test_arabic_script_target_prefers_arabic_font(self, tmp_path, monkeypatch):
|
||||
@@ -1054,3 +1069,85 @@ class TestPdfCleanModeRtl:
|
||||
# Presentation forms in the output prove both the shaping and the
|
||||
# Arabic-capable font reached the PDF (Helvetica cannot encode them).
|
||||
assert any(0xFB50 <= ord(c) <= 0xFEFF for c in extracted)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Normalisation LTR — un document source RTL traduit vers une cible latine
|
||||
# (ex. persan → français) doit ressortir en lecture gauche-à-droite
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestLtrNormalization:
|
||||
def test_word_rtl_source_latin_target_strips_rtl(self, tmp_path):
|
||||
"""Persan → français : bidi/rtl/bidiVisual hérités de la source
|
||||
sont retirés (corps, section, note de bas de page, tableau)."""
|
||||
# 1) Fabrication d'un document RTL complet (cible fa)
|
||||
t_fa = WordTranslator(
|
||||
provider=MockTranslationProvider({"Hello": "FA1", "Extra": "FA2"})
|
||||
)
|
||||
doc = Document()
|
||||
doc.add_paragraph("Hello")
|
||||
table = doc.add_table(rows=1, cols=2)
|
||||
table.cell(0, 0).text = "Extra"
|
||||
fa_in = tmp_path / "in.docx"
|
||||
doc.save(fa_in)
|
||||
_inject_footnotes_part(fa_in)
|
||||
fa_out = tmp_path / "fa.docx"
|
||||
t_fa.translate_file(fa_in, fa_out, "fa")
|
||||
|
||||
# Sanity : la sortie fa porte bien les marques RTL
|
||||
fa_doc = etree.fromstring(_read_zip_entry(fa_out, "word/document.xml"))
|
||||
assert fa_doc.find(f".//{{{W_NS}}}bidi") is not None
|
||||
fa_notes = etree.fromstring(_read_zip_entry(fa_out, "word/footnotes.xml"))
|
||||
assert fa_notes.find(f".//{{{W_NS}}}bidi") is not None
|
||||
|
||||
# 2) Traduction de ce document RTL vers le français
|
||||
t_fr = WordTranslator(
|
||||
provider=MockTranslationProvider({"FA1": "Bonjour", "FA2": "ExtraFR"})
|
||||
)
|
||||
fr_out = tmp_path / "fr.docx"
|
||||
t_fr.translate_file(fa_out, fr_out, "fr")
|
||||
|
||||
fr_doc = etree.fromstring(_read_zip_entry(fr_out, "word/document.xml"))
|
||||
assert fr_doc.find(f".//{{{W_NS}}}bidi") is None
|
||||
assert fr_doc.find(f".//{{{W_NS}}}rtl") is None
|
||||
assert fr_doc.find(f".//{{{W_NS}}}bidiVisual") is None
|
||||
fr_notes = etree.fromstring(_read_zip_entry(fr_out, "word/footnotes.xml"))
|
||||
assert fr_notes.find(f".//{{{W_NS}}}bidi") is None
|
||||
# La traduction française est bien là
|
||||
assert "Bonjour" in "".join(t.text or "" for t in fr_doc.iter(f"{{{W_NS}}}t"))
|
||||
|
||||
def test_excel_rtl_source_latin_target_restores_ltr(self, tmp_path):
|
||||
wb = Workbook()
|
||||
wb.active["A1"] = "سلام"
|
||||
wb.active.sheet_view.rightToLeft = True # feuille d'origine persane
|
||||
input_file = tmp_path / "in.xlsx"
|
||||
wb.save(input_file)
|
||||
|
||||
out = tmp_path / "out.xlsx"
|
||||
ExcelTranslator(provider=MockTranslationProvider({"سلام": "Bonjour"})).translate_file(
|
||||
input_file, out, "fr"
|
||||
)
|
||||
assert not load_workbook(out).active.sheet_view.rightToLeft
|
||||
|
||||
def test_pptx_rtl_source_latin_target_strips_rtl(self, tmp_path):
|
||||
prs = Presentation()
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
box = slide.shapes.add_textbox(Inches(1), Inches(1), Inches(4), Inches(1))
|
||||
para = box.text_frame.paragraphs[0]
|
||||
para.text = "سلام"
|
||||
# Simule un paragraphe d'origine persane
|
||||
pPr = para._p.get_or_add_pPr()
|
||||
pPr.set("rtl", "1")
|
||||
input_file = tmp_path / "in.pptx"
|
||||
prs.save(input_file)
|
||||
|
||||
out = tmp_path / "out.pptx"
|
||||
PowerPointTranslator(
|
||||
provider=MockTranslationProvider({"سلام": "Bonjour"})
|
||||
).translate_file(input_file, out, "fr")
|
||||
|
||||
out_para = _find_pptx_paragraph(Presentation(str(out)), "Bonjour")
|
||||
assert out_para is not None
|
||||
out_pPr = out_para._p.find(f"{{{A_NS}}}pPr")
|
||||
assert out_pPr is None or out_pPr.get("rtl") != "1"
|
||||
|
||||
Reference in New Issue
Block a user