From 028f5027504611af5abe45ab0de726a157775aca Mon Sep 17 00:00:00 2001 From: RAprogramm Date: Sat, 18 Oct 2025 11:28:11 +0700 Subject: [PATCH 1/2] #256 refactor: remove emojis from README headers per protocol Remove all emojis from section headers in README.template.md and regenerate README.md. Emojis should only be used when explicitly requested by user per AI Development Protocol. --- README.md | 52 +++++++++++++++++++++++----------------------- README.template.md | 52 +++++++++++++++++++++++----------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 84e4dee..f86e1ce 100644 --- a/README.md +++ b/README.md @@ -27,24 +27,24 @@ SPDX-License-Identifier: MIT --- -## 📑 Table of Contents - -- [🚀 Overview](#-overview) -- [✨ Highlights](#-highlights) -- [📦 Workspace Crates](#-workspace-crates) -- [🎯 Feature Flags](#-feature-flags) -- [📥 Installation](#-installation) -- [⚡ Benchmarks](#-benchmarks) -- [📊 Code Coverage](#-code-coverage) -- [🎮 Quick Start](#-quick-start) -- [🔧 Advanced Usage](#-advanced-usage) -- [📚 Resources](#-resources) -- [📈 Metrics](#-metrics) -- [📜 License](#-license) +## Table of Contents + +- [Overview](#overview) +- [Highlights](#highlights) +- [Workspace Crates](#workspace-crates) +- [Feature Flags](#feature-flags) +- [Installation](#installation) +- [Benchmarks](#benchmarks) +- [Code Coverage](#code-coverage) +- [Quick Start](#quick-start) +- [Advanced Usage](#advanced-usage) +- [Resources](#resources) +- [Metrics](#metrics) +- [License](#license) --- -## 🚀 Overview +## Overview `masterror` grew from a handful of helpers into a workspace of composable crates for building consistent, observable error surfaces across Rust services. The core @@ -65,7 +65,7 @@ of redaction and metadata. --- -## ✨ Highlights +## Highlights - **Unified taxonomy.** `AppError`, `AppErrorKind` and `AppCode` model domain and transport concerns with conservative HTTP/gRPC mappings, turnkey retry/auth @@ -102,7 +102,7 @@ of redaction and metadata. --- -## 📦 Workspace Crates +## Workspace Crates | Crate | What it provides | When to depend on it | | --- | --- | --- | @@ -122,7 +122,7 @@ of redaction and metadata. --- -## 🎯 Feature Flags +## Feature Flags Pick only what you need; everything is off by default. @@ -150,7 +150,7 @@ The build script keeps the full feature snippet below in sync with --- -## 📥 Installation +## Installation ~~~toml [dependencies] @@ -178,7 +178,7 @@ masterror = { version = "0.24.19", default-features = false } --- -## ⚡ Benchmarks +## Benchmarks Criterion benchmarks cover the hottest conversion paths so regressions are visible before shipping. Run them locally with: @@ -211,7 +211,7 @@ throughput for tighter confidence intervals when investigating changes. --- -## 📊 Code Coverage +## Code Coverage [![codecov](https://codecov.io/gh/RAprogramm/masterror/branch/main/graph/badge.svg?token=V9JQDTZLXH)](https://app.codecov.io/gh/RAprogramm/masterror) @@ -249,7 +249,7 @@ Hierarchical view starting with the entire project at the top, drilling down thr --- -## 🎮 Quick Start +## Quick Start
Create an error @@ -297,7 +297,7 @@ fn do_work(flag: bool) -> AppResult<()> { --- -## 🔧 Advanced Usage +## Advanced Usage
Fail fast without sacrificing typing @@ -601,7 +601,7 @@ assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED"); --- -## 📚 Resources +## Resources - Explore the [error-handling wiki](docs/wiki/index.md) for step-by-step guides, comparisons with `thiserror`/`anyhow`, and troubleshooting recipes. @@ -622,7 +622,7 @@ assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED"); --- -## 📈 Metrics +## Metrics ![Metrics](https://github.com/RAprogramm/infra-metrics-renderer/blob/main/metrics/masterror.svg) @@ -638,7 +638,7 @@ assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED"); --- -## 📜 License +## License MSRV: **1.90** · License: **MIT OR Apache-2.0** · No `unsafe` diff --git a/README.template.md b/README.template.md index 2dc957e..4d75945 100644 --- a/README.template.md +++ b/README.template.md @@ -27,24 +27,24 @@ SPDX-License-Identifier: MIT --- -## 📑 Table of Contents - -- [🚀 Overview](#-overview) -- [✨ Highlights](#-highlights) -- [📦 Workspace Crates](#-workspace-crates) -- [🎯 Feature Flags](#-feature-flags) -- [📥 Installation](#-installation) -- [⚡ Benchmarks](#-benchmarks) -- [📊 Code Coverage](#-code-coverage) -- [🎮 Quick Start](#-quick-start) -- [🔧 Advanced Usage](#-advanced-usage) -- [📚 Resources](#-resources) -- [📈 Metrics](#-metrics) -- [📜 License](#-license) +## Table of Contents + +- [Overview](#overview) +- [Highlights](#highlights) +- [Workspace Crates](#workspace-crates) +- [Feature Flags](#feature-flags) +- [Installation](#installation) +- [Benchmarks](#benchmarks) +- [Code Coverage](#code-coverage) +- [Quick Start](#quick-start) +- [Advanced Usage](#advanced-usage) +- [Resources](#resources) +- [Metrics](#metrics) +- [License](#license) --- -## 🚀 Overview +## Overview `masterror` grew from a handful of helpers into a workspace of composable crates for building consistent, observable error surfaces across Rust services. The core @@ -65,7 +65,7 @@ of redaction and metadata. --- -## ✨ Highlights +## Highlights - **Unified taxonomy.** `AppError`, `AppErrorKind` and `AppCode` model domain and transport concerns with conservative HTTP/gRPC mappings, turnkey retry/auth @@ -102,7 +102,7 @@ of redaction and metadata. --- -## 📦 Workspace Crates +## Workspace Crates | Crate | What it provides | When to depend on it | | --- | --- | --- | @@ -122,7 +122,7 @@ of redaction and metadata. --- -## 🎯 Feature Flags +## Feature Flags Pick only what you need; everything is off by default. @@ -150,7 +150,7 @@ The build script keeps the full feature snippet below in sync with --- -## 📥 Installation +## Installation ~~~toml [dependencies] @@ -173,7 +173,7 @@ masterror = { version = "{{CRATE_VERSION}}", default-features = false } --- -## ⚡ Benchmarks +## Benchmarks Criterion benchmarks cover the hottest conversion paths so regressions are visible before shipping. Run them locally with: @@ -206,7 +206,7 @@ throughput for tighter confidence intervals when investigating changes. --- -## 📊 Code Coverage +## Code Coverage [![codecov](https://codecov.io/gh/RAprogramm/masterror/branch/main/graph/badge.svg?token=V9JQDTZLXH)](https://app.codecov.io/gh/RAprogramm/masterror) @@ -244,7 +244,7 @@ Hierarchical view starting with the entire project at the top, drilling down thr --- -## 🎮 Quick Start +## Quick Start
Create an error @@ -292,7 +292,7 @@ fn do_work(flag: bool) -> AppResult<()> { --- -## 🔧 Advanced Usage +## Advanced Usage
Fail fast without sacrificing typing @@ -596,7 +596,7 @@ assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED"); --- -## 📚 Resources +## Resources - Explore the [error-handling wiki](docs/wiki/index.md) for step-by-step guides, comparisons with `thiserror`/`anyhow`, and troubleshooting recipes. @@ -617,7 +617,7 @@ assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED"); --- -## 📈 Metrics +## Metrics ![Metrics](https://github.com/RAprogramm/infra-metrics-renderer/blob/main/metrics/masterror.svg) @@ -633,7 +633,7 @@ assert_eq!(problem.grpc.expect("grpc").name, "UNAUTHENTICATED"); --- -## 📜 License +## License MSRV: **{{MSRV}}** · License: **MIT OR Apache-2.0** · No `unsafe` From 198a130329f0af8cf621b06dd22e5b87dc36b144 Mon Sep 17 00:00:00 2001 From: RAprogramm Date: Sat, 18 Oct 2025 11:34:50 +0700 Subject: [PATCH 2/2] #256 feat: add professional auto-translation workflow for README Add automated translation system with smart format preservation: - Workflow translates README.md to multiple languages automatically - Currently enabled: Russian (ru), Chinese (zh-CN) - Preserves code blocks, links, badges, technical terms, formatting - Easy to add more languages by uncommenting matrix entries - Translations auto-committed with [skip ci] to avoid loops - Professional Python script with retry logic and placeholder system --- .github/workflows/translate-readme.yml | 269 +++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 1 + README.template.md | 1 + 4 files changed, 272 insertions(+) create mode 100644 .github/workflows/translate-readme.yml diff --git a/.github/workflows/translate-readme.yml b/.github/workflows/translate-readme.yml new file mode 100644 index 0000000..0f74c4a --- /dev/null +++ b/.github/workflows/translate-readme.yml @@ -0,0 +1,269 @@ +name: Translate README + +on: + push: + branches: + - main + paths: + - 'README.md' + - 'README.template.md' + - '.github/workflows/translate-readme.yml' + - '.github/scripts/translate_readme.py' + workflow_dispatch: + +permissions: + contents: write + +jobs: + translate: + runs-on: ubuntu-latest + strategy: + matrix: + language: + - code: ru + name: Russian + - code: zh-CN + name: Chinese + # Uncomment to enable more languages: + # - code: es + # name: Spanish + # - code: de + # name: German + # - code: fr + # name: French + # - code: ja + # name: Japanese + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install googletrans==4.0.0rc1 + + - name: Create translation script + run: | + mkdir -p .github/scripts + cat > .github/scripts/translate_readme.py << 'EOF' + #!/usr/bin/env python3 + """ + Professional README translation script with format preservation. + + Preserves: + - Code blocks (```...```) + - Inline code (`...`) + - Links and URLs + - Badges and images + - HTML tags + - Technical terms (configurable) + - Markdown formatting + """ + + import re + import sys + import time + from typing import List, Tuple + from googletrans import Translator + + # Technical terms that should NOT be translated + PRESERVED_TERMS = { + 'masterror', 'AppError', 'AppErrorKind', 'AppCode', 'ErrorResponse', + 'axum', 'actix', 'tonic', 'sqlx', 'tokio', 'reqwest', 'redis', + 'serde', 'tracing', 'metrics', 'backtrace', 'MSRV', 'HTTP', 'gRPC', + 'JSON', 'API', 'CLI', 'SDK', 'WASM', 'OpenAPI', 'RFC7807', + 'GitHub', 'Cargo', 'Rust', 'workspace', 'derive', 'macro', + 'ProblemJson', 'Metadata', 'Result', 'Option', 'Arc', 'Box', + } + + class SmartTranslator: + def __init__(self, target_lang: str): + self.translator = Translator() + self.target_lang = target_lang + self.placeholders = [] + + def preserve_element(self, text: str) -> str: + """Store element and return placeholder.""" + idx = len(self.placeholders) + self.placeholders.append(text) + return f'___PRESERVE_{idx}___' + + def restore_elements(self, text: str) -> str: + """Restore all preserved elements.""" + for idx, element in enumerate(self.placeholders): + text = text.replace(f'___PRESERVE_{idx}___', element) + return text + + def translate_text(self, text: str, retry=3) -> str: + """Translate with retry logic.""" + for attempt in range(retry): + try: + result = self.translator.translate( + text, + src='en', + dest=self.target_lang + ) + return result.text + except Exception as e: + if attempt < retry - 1: + time.sleep(1) + continue + print(f"Translation failed: {e}", file=sys.stderr) + return text + return text + + def process_line(self, line: str) -> str: + """Process a single line preserving formatting.""" + if not line.strip(): + return line + + # Preserve HTML tags + line = re.sub(r'<[^>]+>', lambda m: self.preserve_element(m.group(0)), line) + + # Preserve badges and images + line = re.sub(r'!\[([^\]]*)\]\([^\)]+\)', lambda m: self.preserve_element(m.group(0)), line) + + # Preserve links (but translate link text) + def handle_link(match): + text, url = match.groups() + # Don't translate if it's a technical term + if text.strip() in PRESERVED_TERMS or text.strip().startswith('`'): + return self.preserve_element(match.group(0)) + # Translate link text but preserve URL + url_placeholder = self.preserve_element(f']({url})') + return f'[{text}' + url_placeholder + + line = re.sub(r'\[([^\]]+)\]\(([^\)]+)\)', handle_link, line) + + # Preserve inline code + line = re.sub(r'`[^`\n]+`', lambda m: self.preserve_element(m.group(0)), line) + + # Preserve technical terms + for term in PRESERVED_TERMS: + pattern = r'\b' + re.escape(term) + r'\b' + line = re.sub(pattern, lambda m: self.preserve_element(m.group(0)), line, flags=re.IGNORECASE) + + # Handle headers + header_match = re.match(r'^(#{1,6})\s+(.+)$', line) + if header_match: + level, text = header_match.groups() + translated = self.translate_text(text) + return f'{level} {translated}' + + # Handle list items + list_match = re.match(r'^(\s*[-*+]\s+)(.+)$', line) + if list_match: + prefix, text = list_match.groups() + translated = self.translate_text(text) + return f'{prefix}{translated}' + + # Translate regular text + return self.translate_text(line) + + def translate_file(self, source_path: str, target_path: str, lang_name: str): + """Translate entire README file.""" + print(f"🌐 Translating README to {lang_name}...") + + with open(source_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Preserve code blocks + code_blocks = [] + def save_code(match): + code_blocks.append(match.group(0)) + return f'___CODE_BLOCK_{len(code_blocks)-1}___' + + content = re.sub(r'```[\s\S]*?```', save_code, content) + + # Process line by line + lines = content.split('\n') + translated_lines = [] + + for i, line in enumerate(lines): + if i % 10 == 0: + print(f"Progress: {i}/{len(lines)} lines", end='\r', file=sys.stderr) + + processed = self.process_line(line) + translated_lines.append(processed) + + result = '\n'.join(translated_lines) + + # Restore code blocks + for i, block in enumerate(code_blocks): + result = result.replace(f'___CODE_BLOCK_{i}___', block) + + # Restore all preserved elements + result = self.restore_elements(result) + + # Add header notice + notice = f""" + + """ + + with open(target_path, 'w', encoding='utf-8') as f: + f.write(notice + result) + + print(f"\n✅ Translation complete: {target_path}") + + if __name__ == '__main__': + if len(sys.argv) != 4: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + + lang_code = sys.argv[1] + lang_name = sys.argv[2] + source = sys.argv[3] + target = f'README.{lang_code}.md' + + translator = SmartTranslator(lang_code) + translator.translate_file(source, target, lang_name) + EOF + + chmod +x .github/scripts/translate_readme.py + + - name: Translate README + run: | + python3 .github/scripts/translate_readme.py \ + "${{ matrix.language.code }}" \ + "${{ matrix.language.name }}" \ + "README.md" + + - name: Check for changes + id: changes + run: | + if git diff --quiet "README.${{ matrix.language.code }}.md"; then + echo "changed=false" >> $GITHUB_OUTPUT + else + echo "changed=true" >> $GITHUB_OUTPUT + fi + + - name: Commit translation + if: steps.changes.outputs.changed == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add "README.${{ matrix.language.code }}.md" + git commit -m "chore: auto-translate README to ${{ matrix.language.name }} [skip ci]" + git pull --rebase origin main + git push origin main diff --git a/Cargo.toml b/Cargo.toml index 23e7f28..af8f1c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ include = [ "tests/**", "README.md", "README.ru.md", + "README.zh-CN.md", "README.template.md", "CHANGELOG.md", "LICENSE-APACHE", diff --git a/README.md b/README.md index f86e1ce..b035ec7 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ SPDX-License-Identifier: MIT [![Hits-of-Code](https://hitsofcode.com/github/RAprogramm/masterror?branch=main)](https://hitsofcode.com/github/RAprogramm/masterror/view?branch=main) > 🇷🇺 [Читайте README на русском языке](README.ru.md) + > 🇨🇳 [中文版 README](README.zh-CN.md) diff --git a/README.template.md b/README.template.md index 4d75945..61f34a6 100644 --- a/README.template.md +++ b/README.template.md @@ -22,6 +22,7 @@ SPDX-License-Identifier: MIT [![Hits-of-Code](https://hitsofcode.com/github/RAprogramm/masterror?branch=main)](https://hitsofcode.com/github/RAprogramm/masterror/view?branch=main) > 🇷🇺 [Читайте README на русском языке](README.ru.md) + > 🇨🇳 [中文版 README](README.zh-CN.md)