mirror of
https://github.com/Azure/co-op-translator
synced 2026-08-09 12:00:08 +00:00
Core: Fix image link prefix depth and normalize webp handling (#354)
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# Different approaches are used for RGB and RGBA images
|
||||
RGBA_IMAGE_EXTENSIONS = {".png"}
|
||||
RGB_IMAGE_EXTENSIONS = {".jpg", ".jpeg"}
|
||||
SUPPORTED_IMAGE_EXTENSIONS = RGBA_IMAGE_EXTENSIONS.union(RGB_IMAGE_EXTENSIONS)
|
||||
|
||||
# WebP is used for translated images (supports both lossy and lossless compression)
|
||||
WEBP_EXTENSION = ".webp"
|
||||
SUPPORTED_IMAGE_EXTENSIONS = RGBA_IMAGE_EXTENSIONS.union(RGB_IMAGE_EXTENSIONS)
|
||||
|
||||
# All input image formats that can be translated (includes WebP for re-processing)
|
||||
TRANSLATABLE_IMAGE_EXTENSIONS = SUPPORTED_IMAGE_EXTENSIONS.union({WEBP_EXTENSION})
|
||||
SUPPORTED_IMAGE_EXTENSIONS = SUPPORTED_IMAGE_EXTENSIONS.union({WEBP_EXTENSION})
|
||||
|
||||
# Supported notebook file extensions
|
||||
SUPPORTED_NOTEBOOK_EXTENSIONS = {".ipynb"}
|
||||
|
||||
@@ -32,7 +32,10 @@ from co_op_translator.config.constants import SUPPORTED_MARKDOWN_EXTENSIONS
|
||||
from co_op_translator.core.llm.markdown_translator import MarkdownTranslator
|
||||
from co_op_translator.core.project.directory_manager import DirectoryManager
|
||||
from co_op_translator.utils.common.task_utils import worker
|
||||
from co_op_translator.utils.llm.markdown_utils import compare_line_breaks
|
||||
from co_op_translator.utils.llm.markdown_utils import (
|
||||
compare_line_breaks,
|
||||
update_image_links,
|
||||
)
|
||||
from co_op_translator.utils.common.metadata_utils import is_notebook_up_to_date
|
||||
from co_op_translator.config.base_config import Config
|
||||
from co_op_translator.utils.common.file_utils import (
|
||||
@@ -1060,13 +1063,13 @@ class TranslationManager:
|
||||
rel_to_lang = md_translated.relative_to(lang_dir)
|
||||
original_md_path = (self.root_dir / rel_to_lang).resolve()
|
||||
|
||||
# Use existing update_image_links with resolved paths to get correct links
|
||||
updated = self.markdown_translator.update_image_links(
|
||||
# Use module-level update_image_links with resolved paths to get correct links
|
||||
updated = update_image_links(
|
||||
content,
|
||||
original_md_path,
|
||||
lang_code,
|
||||
translations_dir=self.translations_dir,
|
||||
image_dir=self.image_dir,
|
||||
translated_images_dir=self.image_dir,
|
||||
root_dir=self.root_dir,
|
||||
use_translated_images=True,
|
||||
)
|
||||
|
||||
@@ -660,11 +660,15 @@ def update_image_links(
|
||||
# Target translated markdown directory structure: translations/<lang>/<relative_path_to_parent>
|
||||
# The translated file will be saved at: translations_dir / language_code / (md_file_path relative to root_dir)
|
||||
# Its directory is:
|
||||
translated_md_dir = (
|
||||
translations_dir
|
||||
/ language_code
|
||||
/ md_file_path.relative_to(root_dir).parent
|
||||
).resolve()
|
||||
try:
|
||||
_ = md_file_path.relative_to(translations_dir / language_code)
|
||||
translated_md_dir = md_file_path.parent.resolve()
|
||||
except Exception:
|
||||
translated_md_dir = (
|
||||
translations_dir
|
||||
/ language_code
|
||||
/ md_file_path.relative_to(root_dir).parent
|
||||
).resolve()
|
||||
|
||||
if not use_translated_images:
|
||||
# Link to original image when using original images
|
||||
@@ -688,34 +692,52 @@ def update_image_links(
|
||||
).replace(os.path.sep, "/")
|
||||
logger.info(f"Using original image link: {updated_link}")
|
||||
else:
|
||||
# Link to translated image when not in markdown-only mode
|
||||
# We need to handle both root-relative and regular paths
|
||||
try:
|
||||
# Pass root_dir to get_actual_image_path to properly handle root-relative paths
|
||||
if path.startswith("/"):
|
||||
# For root-relative paths, we need to use the root_dir
|
||||
logger.info(
|
||||
f"Root-relative path detected in non-markdown-only mode: {path}"
|
||||
)
|
||||
# Use the modified get_actual_image_path that accepts root_dir
|
||||
actual_image_path = get_actual_image_path(
|
||||
path, md_file_path, root_dir
|
||||
base_names = {
|
||||
translated_images_dir.name,
|
||||
"translated_images",
|
||||
"translated_images_fast",
|
||||
}
|
||||
parts = path.split("/")
|
||||
if (
|
||||
len(parts) >= 3
|
||||
and parts[-3] in base_names
|
||||
and parts[-2] == language_code
|
||||
):
|
||||
rel_path = os.path.relpath(
|
||||
translated_images_dir.resolve(), translated_md_dir
|
||||
)
|
||||
updated_link = os.path.join(
|
||||
rel_path,
|
||||
language_code,
|
||||
parts[-1],
|
||||
).replace(os.path.sep, "/")
|
||||
else:
|
||||
# No change for regular paths
|
||||
actual_image_path = get_actual_image_path(
|
||||
path, md_file_path
|
||||
)
|
||||
# Pass root_dir to get_actual_image_path to properly handle root-relative paths
|
||||
if path.startswith("/"):
|
||||
# For root-relative paths, we need to use the root_dir
|
||||
logger.info(
|
||||
f"Root-relative path detected in non-markdown-only mode: {path}"
|
||||
)
|
||||
# Use the modified get_actual_image_path that accepts root_dir
|
||||
actual_image_path = get_actual_image_path(
|
||||
path, md_file_path, root_dir
|
||||
)
|
||||
else:
|
||||
# No change for regular paths
|
||||
actual_image_path = get_actual_image_path(
|
||||
path, md_file_path
|
||||
)
|
||||
|
||||
rel_path = os.path.relpath(
|
||||
translated_images_dir.resolve(), translated_md_dir
|
||||
)
|
||||
new_filename = generate_translated_filename(
|
||||
actual_image_path, language_code, root_dir
|
||||
)
|
||||
updated_link = os.path.join(
|
||||
rel_path, language_code, new_filename
|
||||
).replace(os.path.sep, "/")
|
||||
rel_path = os.path.relpath(
|
||||
translated_images_dir.resolve(), translated_md_dir
|
||||
)
|
||||
new_filename = generate_translated_filename(
|
||||
actual_image_path, language_code, root_dir
|
||||
)
|
||||
updated_link = os.path.join(
|
||||
rel_path, language_code, new_filename
|
||||
).replace(os.path.sep, "/")
|
||||
logger.info(f"Using translated image link: {updated_link}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing image path {path}: {e}")
|
||||
@@ -755,11 +777,15 @@ def update_image_links(
|
||||
return match.group(0)
|
||||
|
||||
try:
|
||||
translated_md_dir = (
|
||||
translations_dir
|
||||
/ language_code
|
||||
/ md_file_path.relative_to(root_dir).parent
|
||||
).resolve()
|
||||
try:
|
||||
_ = md_file_path.relative_to(translations_dir / language_code)
|
||||
translated_md_dir = md_file_path.parent.resolve()
|
||||
except Exception:
|
||||
translated_md_dir = (
|
||||
translations_dir
|
||||
/ language_code
|
||||
/ md_file_path.relative_to(root_dir).parent
|
||||
).resolve()
|
||||
|
||||
if not use_translated_images:
|
||||
# Link to original image when using original images
|
||||
@@ -771,23 +797,42 @@ def update_image_links(
|
||||
original_linked_file_path, translated_md_dir
|
||||
).replace(os.path.sep, "/")
|
||||
else:
|
||||
# Link to translated image when using translated images
|
||||
if path.startswith("/"):
|
||||
actual_image_path = get_actual_image_path(
|
||||
path, md_file_path, root_dir
|
||||
base_names = {
|
||||
translated_images_dir.name,
|
||||
"translated_images",
|
||||
"translated_images_fast",
|
||||
}
|
||||
parts = path.split("/")
|
||||
if (
|
||||
len(parts) >= 3
|
||||
and parts[-3] in base_names
|
||||
and parts[-2] == language_code
|
||||
):
|
||||
rel_path = os.path.relpath(
|
||||
translated_images_dir.resolve(), translated_md_dir
|
||||
)
|
||||
updated_src = os.path.join(
|
||||
rel_path,
|
||||
language_code,
|
||||
parts[-1],
|
||||
).replace(os.path.sep, "/")
|
||||
else:
|
||||
actual_image_path = get_actual_image_path(path, md_file_path)
|
||||
if path.startswith("/"):
|
||||
actual_image_path = get_actual_image_path(
|
||||
path, md_file_path, root_dir
|
||||
)
|
||||
else:
|
||||
actual_image_path = get_actual_image_path(path, md_file_path)
|
||||
|
||||
rel_path = os.path.relpath(
|
||||
translated_images_dir.resolve(), translated_md_dir
|
||||
)
|
||||
new_filename = generate_translated_filename(
|
||||
actual_image_path, language_code, root_dir
|
||||
)
|
||||
updated_src = os.path.join(
|
||||
rel_path, language_code, new_filename
|
||||
).replace(os.path.sep, "/")
|
||||
rel_path = os.path.relpath(
|
||||
translated_images_dir.resolve(), translated_md_dir
|
||||
)
|
||||
new_filename = generate_translated_filename(
|
||||
actual_image_path, language_code, root_dir
|
||||
)
|
||||
updated_src = os.path.join(
|
||||
rel_path, language_code, new_filename
|
||||
).replace(os.path.sep, "/")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing HTML <img> path {src}: {e}")
|
||||
updated_src = src
|
||||
|
||||
@@ -569,6 +569,37 @@ def test_image_path_depth_in_nested_translations(complex_dir_structure):
|
||||
assert "../../../../../" not in result
|
||||
|
||||
|
||||
def test_webp_translated_images_prefix_depth_is_normalized(complex_dir_structure):
|
||||
root_dir = complex_dir_structure
|
||||
translations_dir = root_dir / "translations"
|
||||
translated_images_dir = root_dir / "translated_images"
|
||||
translations_dir.mkdir(parents=True, exist_ok=True)
|
||||
translated_images_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
md_rel_path = Path("15-rag-and-vector-databases/README.md")
|
||||
md_file_path = root_dir / md_rel_path
|
||||
md_file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
language_code = "et"
|
||||
# Intentionally wrong depth (one extra ../) pointing to an already-translated WebP
|
||||
markdown_content = (
|
||||
""
|
||||
)
|
||||
|
||||
result = update_image_links(
|
||||
markdown_content,
|
||||
md_file_path,
|
||||
language_code,
|
||||
translations_dir,
|
||||
translated_images_dir,
|
||||
root_dir,
|
||||
use_translated_images=True,
|
||||
)
|
||||
|
||||
assert "../../../translated_images/et/foo.1234567890abcdef.webp" in result
|
||||
assert "../../../../translated_images/et/foo.1234567890abcdef.webp" not in result
|
||||
|
||||
|
||||
def test_update_notebook_links_prefers_translated(tmp_path):
|
||||
"""When translated notebook exists, link should point to translated notebook relative to translated md dir."""
|
||||
root_dir = tmp_path
|
||||
|
||||
Reference in New Issue
Block a user