diff --git a/src/co_op_translator/cli/translate.py b/src/co_op_translator/cli/translate.py index 315df4f..f008866 100644 --- a/src/co_op_translator/cli/translate.py +++ b/src/co_op_translator/cli/translate.py @@ -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__) @@ -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: @@ -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}...") diff --git a/src/co_op_translator/templates/other_courses.md b/src/co_op_translator/templates/other_courses.md new file mode 100644 index 0000000..4ca8c4d --- /dev/null +++ b/src/co_op_translator/templates/other_courses.md @@ -0,0 +1,20 @@ + +- [**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) + diff --git a/src/co_op_translator/utils/common/file_utils.py b/src/co_op_translator/utils/common/file_utils.py index e85d339..c1d749c 100644 --- a/src/co_op_translator/utils/common/file_utils.py +++ b/src/co_op_translator/utils/common/file_utils.py @@ -9,22 +9,26 @@ LANG_TABLE_START = "" LANG_TABLE_END = "" +OTHER_COURSES_START = "" +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() @@ -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: @@ -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. @@ -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