Skip to content

Latest commit

 

History

History
121 lines (89 loc) · 3.68 KB

File metadata and controls

121 lines (89 loc) · 3.68 KB

Extensibility Guide

LocaleSync is designed for extension without modification. All major components are behind Protocol-based contracts that new implementations can satisfy.

Built-in Translation Providers

LocaleSync ships with three translation providers:

Provider Default? API Key? Languages Use Case
GoogleTranslator ✅ Yes No 100+ Production use
DemoTranslator No No 5 (pl, de, fr, es, it) Offline/testing
NoopTranslator No No Baseline (returns source text)

Select a provider with --provider:

locale-sync translate ./locales                    # Google Translate (default)
locale-sync translate ./locales --provider demo    # Offline dictionary

Adding a Custom Translation Provider

Implement the TranslationProvider protocol:

# my_provider.py
class DeepLTranslator:
    """DeepL translation provider."""

    def __init__(self, api_key: str) -> None:
        self._api_key = api_key

    @property
    def name(self) -> str:
        return "deepl"

    def translate(self, text: str, source_lang: str, target_lang: str) -> str:
        # Call DeepL API here
        import deepl
        translator = deepl.Translator(self._api_key)
        result = translator.translate_text(text, source_lang=source_lang, target_lang=target_lang)
        return result.text

Wrap it with PlaceholderAwareTranslator for automatic placeholder safety:

from locale_sync.domain.placeholder import PlaceholderManager
from locale_sync.infrastructure.translators.placeholder_aware import PlaceholderAwareTranslator

base = DeepLTranslator(api_key="...")
safe = PlaceholderAwareTranslator(base, PlaceholderManager())

Then inject into the SyncUseCase:

from locale_sync.application.sync_use_case import SyncUseCase

use_case = SyncUseCase(parser=..., writer=..., translator=safe)

Adding a File Format

Implement LocaleParser and LocaleWriter protocols:

class YamlLocaleParser:
    def parse(self, path: Path) -> LocaleData:
        import yaml
        with open(path) as f:
            data = yaml.safe_load(f)
        return LocaleData.from_nested_dict(data)

    def supports(self, path: Path) -> bool:
        return path.suffix.lower() in (".yaml", ".yml")


class YamlLocaleWriter:
    def write(self, path: Path, data: LocaleData, *, sort_keys=False, indent=2, create_backup=False):
        import yaml
        nested = data.to_nested_dict(sort_keys=sort_keys)
        with open(path, "w") as f:
            yaml.dump(nested, f, allow_unicode=True, default_flow_style=False)
        return None

Adding a Report Format

Implement the Reporter protocol:

class CsvReporter:
    def report_scan(self, result: ScanResult) -> str:
        lines = ["file,locale_code"]
        for f in result.locale_files:
            lines.append(f"{f.filename},{f.locale_code}")
        return "\n".join(lines)

    def report_check(self, result: CheckResult) -> str: ...
    def report_sync(self, result: SyncResult) -> str: ...

Adding a Discovery Strategy

class RecursiveMultiFormatDiscoverer:
    def discover(self, directory: Path) -> list[LocaleFile]:
        # Discover .json, .yaml, .po files recursively
        ...

Future Extension Ideas

  • Config file (.localesync.toml) — Load default options
  • Plugin registry — Auto-discover providers via entry points
  • GitHub Actions integration — PR comment generation with check results
  • Include/exclude rules — Filter by key patterns or file patterns
  • Namespace filtering — Sync only specific key prefixes
  • Custom serialization — Control JSON formatting, trailing commas, comments