Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/co_op_translator/cli/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
from co_op_translator.config.vision_config.config import VisionConfig
from co_op_translator.config.llm_config.config import LLMConfig
from co_op_translator.utils.common.logging_utils import setup_logging
from co_op_translator.utils.common.file_utils import update_readme_languages_table
from co_op_translator.utils.common.file_utils import (
update_readme_languages_table,
update_readme_other_courses,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -258,10 +261,10 @@ def translate_command(
language_codes, root_dir, translation_types=translation_types
)

# If all languages are selected, update README languages table BEFORE translation
# Update README shared sections BEFORE translation
readme_path = root_path / "README.md"
try:
if all_languages_selected:
readme_path = root_path / "README.md"
if update_readme_languages_table(readme_path):
click.echo("✅ Updated README languages table from template.")
else:
Expand All @@ -271,6 +274,12 @@ def translate_command(
except Exception as e:
logger.warning(f"Failed to update README languages table: {e}")

try:
if update_readme_other_courses(readme_path):
click.echo("✅ Updated README 'Other courses' section from template.")
except Exception as e:
logger.warning(f"Failed to update README 'Other courses': {e}")

if fix:
click.echo(f"Fixing translations with confidence below {min_confidence}...")

Expand Down
20 changes: 20 additions & 0 deletions src/co_op_translator/templates/other_courses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- CO-OP TRANSLATOR OTHER COURSES START -->
- [**NEW** AZD for Beginners](https://github.com/microsoft/AZD-for-beginners?WT.mc_id=academic-105485-koreyst)
- [**NEW** Edge AI for Beginners](https://github.com/microsoft/edgeai-for-beginners?WT.mc_id=academic-105485-koreyst)
- [Model Context Protocol (MCP) For Beginners](https://github.com/microsoft/mcp-for-beginners?WT.mc_id=academic-105485-koreyst)
- [AI Agents for Beginners](https://github.com/microsoft/ai-agents-for-beginners?WT.mc_id=academic-105485-koreyst)
- [Generative AI for Beginners](https://github.com/microsoft/generative-ai-for-beginners?WT.mc_id=academic-105485-koreyst)
- [Generative AI for Beginners with .NET](https://github.com/microsoft/Generative-AI-for-beginners-dotnet?WT.mc_id=academic-105485-koreyst)
- [Generative AI for Beginners with Java](https://github.com/microsoft/generative-ai-for-beginners-java?WT.mc_id=academic-105485-koreyst)
- [Generative AI for Beginners with JavaScript](https://github.com/microsoft/generative-ai-with-javascript?WT.mc_id=academic-105485-koreyst)
- [ML for Beginners](https://akams/ml-beginners?WT.mc_id=academic-105485-koreyst)
- [Data Science for Beginners](https://aka.ms/datascience-beginners?WT.mc_id=academic-105485-koreyst)
- [AI for Beginners](https://aka.ms/ai-beginners?WT.mc_id=academic-105485-koreyst)
- [Cybersecurity for Beginners](https://github.com/microsoft/Security-101?WT.mc_id=academic-96948-sayoung)
- [Web Dev for Beginners](https://aka.ms/webdev-beginners?WT.mc_id=academic-105485-koreyst)
- [IoT for Beginners](https://aka.ms/iot-beginners?WT.mc_id=academic-105485-koreyst)
- [XR Development for Beginners](https://github.com/microsoft/xr-development-for-beginners?WT.mc_id=academic-105485-koreyst)
- [Mastering GitHub Copilot for AI Paired Programming](https://aka.ms/GitHubCopilotAI?WT.mc_id=academic-105485-koreyst)
- [Mastering GitHub Copilot for C#/.NET Developers](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers?WT.mc_id=academic-105485-koreyst)
- [Choose Your Own Copilot Adventure](https://github.com/microsoft/CopilotAdventures?WT.mc_id=academic-105485-koreyst)
<!-- CO-OP TRANSLATOR OTHER COURSES END -->
60 changes: 54 additions & 6 deletions src/co_op_translator/utils/common/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,26 @@

LANG_TABLE_START = "<!-- CO-OP TRANSLATOR LANGUAGES TABLE START -->"
LANG_TABLE_END = "<!-- CO-OP TRANSLATOR LANGUAGES TABLE END -->"
OTHER_COURSES_START = "<!-- CO-OP TRANSLATOR OTHER COURSES START -->"
OTHER_COURSES_END = "<!-- CO-OP TRANSLATOR OTHER COURSES END -->"


def replace_between_markers(readme_text: str, new_block: str) -> str:
def _replace_between_markers_generic(
readme_text: str, new_block: str, start_marker: str, end_marker: str
) -> str:
"""
Replace content between language table markers with the provided new block.
Replace content between the provided start/end markers with the new block.
Markers are included in the replacement. If markers are missing, returns original text.
"""
# Case-insensitive detection of markers so users can vary casing
lower_text = readme_text.lower()
start_idx = lower_text.find(LANG_TABLE_START.lower())
end_idx = lower_text.find(LANG_TABLE_END.lower())
start_idx = lower_text.find(start_marker.lower())
end_idx = lower_text.find(end_marker.lower())
if start_idx == -1 or end_idx == -1 or end_idx < start_idx:
return readme_text

# Expand end to include the end marker line completely
end_idx_inclusive = end_idx + len(LANG_TABLE_END)
end_idx_inclusive = end_idx + len(end_marker)

before = readme_text[:start_idx].rstrip()
after = readme_text[end_idx_inclusive:].lstrip()
Expand All @@ -39,6 +43,15 @@ def replace_between_markers(readme_text: str, new_block: str) -> str:
return "\n\n".join(pieces) + ("\n" if readme_text.endswith("\n") else "")


def replace_between_markers(readme_text: str, new_block: str) -> str:
"""
Backward-compatible helper that replaces between the LANGUAGES TABLE markers.
"""
return _replace_between_markers_generic(
readme_text, new_block, LANG_TABLE_START, LANG_TABLE_END
)


def load_languages_table_template() -> str:
"""Load the bundled languages table template markdown."""
try:
Expand All @@ -52,6 +65,19 @@ def load_languages_table_template() -> str:
return ""


def load_other_courses_template() -> str:
"""Load the bundled other courses template markdown."""
try:
from importlib import resources

with resources.open_text(
"co_op_translator.templates", "other_courses.md", encoding="utf-8"
) as f:
return f.read()
except Exception:
return ""


def update_readme_languages_table(readme_path: Path) -> bool:
"""
Update README languages table between markers using bundled template.
Expand All @@ -70,7 +96,29 @@ def update_readme_languages_table(readme_path: Path) -> bool:
template,
flags=re.IGNORECASE,
)
updated = replace_between_markers(original, template)
updated = _replace_between_markers_generic(
original, template, LANG_TABLE_START, LANG_TABLE_END
)
if updated != original:
readme_path.write_text(updated, encoding="utf-8", newline="\n")
return True
return False


def update_readme_other_courses(readme_path: Path) -> bool:
"""
Update README 'Other courses' section between markers using bundled template.
Returns True if updated, False otherwise.
"""
if not readme_path.exists():
return False
original = readme_path.read_text(encoding="utf-8")
template = load_other_courses_template()
if not template:
return False
updated = _replace_between_markers_generic(
original, template, OTHER_COURSES_START, OTHER_COURSES_END
)
if updated != original:
readme_path.write_text(updated, encoding="utf-8", newline="\n")
return True
Expand Down