fix(pdf): le texte persan s'affichait en points d'interrogation
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m51s

Cause racine (reproduite et verrouillee par test) : l'insertion PDF
demandait la police integree Helvetica tout en fournissant un fichier
de police — PyMuPDF ignore alors le fichier, et Helvetica n'a aucun
glyphe arabe : chaque lettre persane devenait un '?', et ces '?'
debordaient les boites d'origine (les nombreux « [translation
overflow] »). Defaut present dans toutes les versions deployees.

- le nom de police est desormais Personnalise quand un fichier de
  police est fourni : le fichier est reellement integre
- reproduction complete avant/apres : 0 '?', 0 debordement, persan
  rendu avec la police resolue
- test de non-regression : la page traduite ne contient PAS Helvetica
  et le texte extrait n'a aucun '?'
- bouton « Reconstruire » de la relecture : une infobulle explique
  desormais pourquoi il est grise (aucun segment approuve ni modifie),
  dans les 13 langues
This commit is contained in:
2026-09-01 21:32:40 +02:00
parent 5f3d57b4f7
commit f93b13b53f
16 changed files with 76 additions and 22 deletions

View File

@@ -346,7 +346,16 @@ export default function ReviewPage() {
if (f) importXliff(f);
}}
/>
<Button size="sm" onClick={rebuild} disabled={isRebuilding || counts.approved + counts.edited === 0}>
<Button
size="sm"
onClick={rebuild}
disabled={isRebuilding || counts.approved + counts.edited === 0}
title={
counts.approved + counts.edited === 0
? t('reviews.rebuildDisabledHint')
: undefined
}
>
{isRebuilding ? <Loader2 className="size-3.5 animate-spin" /> : <Download className="size-3.5" />}
{t('reviews.rebuild')}
</Button>

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "لا توجد أجزاء لمراجعتها في هذه المهمة",
"reviews.empty.desc": "لا تحتوي هذه الترجمة على أجزاء محفوظة: إما أنها فشلت قبل ترجمة النص، أو لم يعد الخادم يحتفظ بها. يمكنك إزالتها من قائمة الترجمات الأخيرة."
"reviews.empty.desc": "لا تحتوي هذه الترجمة على أجزاء محفوظة: إما أنها فشلت قبل ترجمة النص، أو لم يعد الخادم يحتفظ بها. يمكنك إزالتها من قائمة الترجمات الأخيرة.",
"reviews.rebuildDisabledHint": "وافق على جزء واحد على الأقل أو عدّله أولاً — تعيد إعادة البناء تطبيق مراجعتك."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "Für diesen Auftrag gibt es nichts zu prüfen",
"reviews.empty.desc": "Diese Übersetzung enthält keine gespeicherten Segmente: Entweder ist sie vor der Textübersetzung fehlgeschlagen, oder der Server bewahrt sie nicht mehr auf. Sie können sie aus der Liste der letzten Übersetzungen entfernen."
"reviews.empty.desc": "Diese Übersetzung enthält keine gespeicherten Segmente: Entweder ist sie vor der Textübersetzung fehlgeschlagen, oder der Server bewahrt sie nicht mehr auf. Sie können sie aus der Liste der letzten Übersetzungen entfernen.",
"reviews.rebuildDisabledHint": "Genehmigen oder bearbeiten Sie zuerst mindestens ein Segment — der Neuaufbau übernimmt Ihre Prüfung."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "Nothing to review for this job",
"reviews.empty.desc": "This translation has no stored segments: either it failed before any text was translated, or it is no longer retained by the server. You can remove it from your recent translations list."
"reviews.empty.desc": "This translation has no stored segments: either it failed before any text was translated, or it is no longer retained by the server. You can remove it from your recent translations list.",
"reviews.rebuildDisabledHint": "Approve or edit at least one segment first — the rebuild applies your reviewed wording."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "Nada que revisar en este trabajo",
"reviews.empty.desc": "Esta traducción no tiene segmentos guardados: o falló antes de traducir el texto, o el servidor ya no la conserva. Puede quitarla de la lista de traducciones recientes."
"reviews.empty.desc": "Esta traducción no tiene segmentos guardados: o falló antes de traducir el texto, o el servidor ya no la conserva. Puede quitarla de la lista de traducciones recientes.",
"reviews.rebuildDisabledHint": "Primero aprueba o edita al menos un segmento: la reconstrucción aplica tu revisión."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "بخشی برای بازبینی در این کار وجود ندارد",
"reviews.empty.desc": "این ترجمه بخش ذخیره‌شده‌ای ندارد: یا پیش از ترجمهٔ متن ناموفق بوده، یا سرور دیگر آن را نگه نمی‌دارد. می‌توانید آن را از فهرست ترجمه‌های اخیر حذف کنید."
"reviews.empty.desc": "این ترجمه بخش ذخیره‌شده‌ای ندارد: یا پیش از ترجمهٔ متن ناموفق بوده، یا سرور دیگر آن را نگه نمی‌دارد. می‌توانید آن را از فهرست ترجمه‌های اخیر حذف کنید.",
"reviews.rebuildDisabledHint": "ابتدا حداقل یک بخش را تأیید یا ویرایش کنید — بازسازی، بازبینی شما را اعمال می‌کند."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approbation ({current}/{total})…",
"reviews.noMatchingSegments": "Aucun segment ne correspond à votre filtre.",
"reviews.empty.title": "Rien à relire pour ce travail",
"reviews.empty.desc": "Cette traduction n'a aucun segment enregistré : soit elle a échoué avant la traduction du texte, soit elle n'est plus conservée par le serveur. Vous pouvez la retirer de la liste des traductions récentes."
"reviews.empty.desc": "Cette traduction n'a aucun segment enregistré : soit elle a échoué avant la traduction du texte, soit elle n'est plus conservée par le serveur. Vous pouvez la retirer de la liste des traductions récentes.",
"reviews.rebuildDisabledHint": "Approuvez ou modifiez d'abord au moins un segment — la reconstruction applique votre relecture."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "Niente da rivedere per questo lavoro",
"reviews.empty.desc": "Questa traduzione non ha segmenti salvati: o è fallita prima di tradurre il testo, o il server non la conserva più. Puoi rimuoverla dall'elenco delle traduzioni recenti."
"reviews.empty.desc": "Questa traduzione non ha segmenti salvati: o è fallita prima di tradurre il testo, o il server non la conserva più. Puoi rimuoverla dall'elenco delle traduzioni recenti.",
"reviews.rebuildDisabledHint": "Prima approva o modifica almeno un segmento: la ricostruzione applica la tua revisione."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "このジョブに校正するセグメントはありません",
"reviews.empty.desc": "この翻訳には保存されたセグメントがありません。テキストの翻訳前に失敗したか、サーバーに保持されていません。最近の翻訳リストから削除できます。"
"reviews.empty.desc": "この翻訳には保存されたセグメントがありません。テキストの翻訳前に失敗したか、サーバーに保持されていません。最近の翻訳リストから削除できます。",
"reviews.rebuildDisabledHint": "まず少なくとも1つのセグメントを承認または編集してください。再構築には校正内容が必要です。"
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "이 작업에 검토할 세그먼트가 없습니다",
"reviews.empty.desc": "이 번역에는 저장된 세그먼트가 없습니다. 텍스트 번역 전에 실패했거나 서버에 더 이상 보관되지 않습니다. 최근 번역 목록에서 제거할 수 있습니다."
"reviews.empty.desc": "이 번역에는 저장된 세그먼트가 없습니다. 텍스트 번역 전에 실패했거나 서버에 더 이상 보관되지 않습니다. 최근 번역 목록에서 제거할 수 있습니다.",
"reviews.rebuildDisabledHint": "먼저 세그먼트를 하나 이상 승인하거나 수정하세요 — 재구성에는 검토 내용이 필요합니다."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "Niets te controleren voor deze opdracht",
"reviews.empty.desc": "Deze vertaling heeft geen opgeslagen segmenten: of hij mislukte voordat er tekst werd vertaald, of de server bewaart hem niet meer. U kunt hem verwijderen uit de lijst met recente vertalingen."
"reviews.empty.desc": "Deze vertaling heeft geen opgeslagen segmenten: of hij mislukte voordat er tekst werd vertaald, of de server bewaart hem niet meer. U kunt hem verwijderen uit de lijst met recente vertalingen.",
"reviews.rebuildDisabledHint": "Keur eerst minstens één segment goed of bewerk het — de herbouw past uw controle toe."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "Nada a revisar neste trabalho",
"reviews.empty.desc": "Esta tradução não tem segmentos armazenados: ou falhou antes de qualquer texto ser traduzido, ou o servidor já não a conserva. Pode removê-la da lista de traduções recentes."
"reviews.empty.desc": "Esta tradução não tem segmentos armazenados: ou falhou antes de qualquer texto ser traduzido, ou o servidor já não a conserva. Pode removê-la da lista de traduções recentes.",
"reviews.rebuildDisabledHint": "Aprove ou edite primeiro pelo menos um segmento — a reconstrução aplica a sua revisão."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "Для этого задания нет сегментов для проверки",
"reviews.empty.desc": "У этого перевода нет сохранённых сегментов: либо он завершился ошибкой до перевода текста, либо сервер его больше не хранит. Вы можете удалить его из списка недавних переводов."
"reviews.empty.desc": "У этого перевода нет сохранённых сегментов: либо он завершился ошибкой до перевода текста, либо сервер его больше не хранит. Вы можете удалить его из списка недавних переводов.",
"reviews.rebuildDisabledHint": "Сначала утвердите или измените хотя бы один сегмент — пересборка применит вашу правку."
}

View File

@@ -49,5 +49,6 @@
"reviews.approvingProgress": "Approving ({current}/{total})…",
"reviews.noMatchingSegments": "No segments match your filter.",
"reviews.empty.title": "此任务没有可校对的段落",
"reviews.empty.desc": "此翻译没有已存储的段落:可能是在翻译文本之前失败,或服务器已不再保留。您可以将其从最近翻译列表中移除。"
"reviews.empty.desc": "此翻译没有已存储的段落:可能是在翻译文本之前失败,或服务器已不再保留。您可以将其从最近翻译列表中移除。",
"reviews.rebuildDisabledHint": "请先批准或修改至少一个段落——重建会应用您的校对内容。"
}