369 lines
12 KiB
Python
369 lines
12 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable
|
|
|
|
SYSTEM_STRING_SPECS = {
|
|
"plan_badge": {
|
|
"sources": ("PLAN",),
|
|
"issue_type": "generic_badge_label",
|
|
"translations": {
|
|
"en": "Package",
|
|
"fr": "FORFAIT",
|
|
"es": "Paquete",
|
|
"ru": "Пакет",
|
|
},
|
|
"canonical_by_locale": {
|
|
"de": ("PLAN",),
|
|
"nl": ("PLAN",),
|
|
"it": ("PIANO",),
|
|
},
|
|
"contexts": {
|
|
"en": {
|
|
"badge": "Package",
|
|
"label": "Package",
|
|
"title": "Package",
|
|
"heading": "Package",
|
|
"rendered": "Package",
|
|
},
|
|
"fr": {
|
|
"badge": "FORFAIT",
|
|
"label": "FORFAIT",
|
|
"title": "FORFAIT",
|
|
"heading": "FORFAIT",
|
|
"rendered": "FORFAIT",
|
|
},
|
|
"es": {
|
|
"badge": "Paquete",
|
|
"label": "Paquete",
|
|
"title": "Paquete",
|
|
"heading": "Paquete",
|
|
"rendered": "Paquete",
|
|
},
|
|
"ru": {
|
|
"badge": "Пакет",
|
|
"label": "Пакет",
|
|
"title": "Пакет",
|
|
"heading": "Пакет",
|
|
"rendered": "Пакет",
|
|
},
|
|
},
|
|
},
|
|
"services_badge": {
|
|
"sources": ("SERVICES",),
|
|
"issue_type": "generic_badge_label",
|
|
"translations": {
|
|
"en": "Services",
|
|
"fr": "PRESTATIONS",
|
|
"pt": "SERVIÇOS",
|
|
},
|
|
"contexts": {
|
|
"en": {
|
|
"badge": "Services",
|
|
"label": "Services",
|
|
"title": "Services",
|
|
"heading": "Services",
|
|
"rendered": "Services",
|
|
},
|
|
"fr": {
|
|
"badge": "PRESTATIONS",
|
|
"label": "PRESTATIONS",
|
|
"title": "PRESTATIONS",
|
|
"heading": "PRESTATIONS",
|
|
"rendered": "PRESTATIONS",
|
|
},
|
|
"pt": {
|
|
"badge": "SERVIÇOS",
|
|
"label": "SERVIÇOS",
|
|
"title": "SERVIÇOS",
|
|
"heading": "SERVIÇOS",
|
|
"rendered": "SERVIÇOS",
|
|
},
|
|
},
|
|
},
|
|
"response_time": {
|
|
"sources": ("Reaktionszeit",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"en": "Response time",
|
|
"fr": "Temps de réponse",
|
|
"es": "Tiempo de respuesta",
|
|
"it": "Tempo di risposta",
|
|
"ru": "Время ответа",
|
|
},
|
|
},
|
|
"average_delivery": {
|
|
"sources": ("Durchschnittliche Lieferung",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"en": "Average delivery time",
|
|
"fr": "Délai moyen de livraison",
|
|
"es": "Plazo medio de entrega",
|
|
"it": "Tempo medio di consegna",
|
|
"ru": "Средний срок запуска",
|
|
},
|
|
},
|
|
"without_commitment": {
|
|
"sources": ("Unverbindlich",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"en": "No obligation",
|
|
"fr": "Sans engagement",
|
|
"es": "Sin compromiso",
|
|
"it": "Senza impegno",
|
|
"pt": "Sem compromisso",
|
|
"ru": "Без обязательств",
|
|
},
|
|
},
|
|
"transparent_label": {
|
|
"sources": ("Transparent",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"en": "Clear",
|
|
"fr": "Clair",
|
|
"es": "Transparente",
|
|
"it": "Chiaro",
|
|
"pt": "Transparente",
|
|
"ru": "Прозрачно",
|
|
},
|
|
"contexts": {
|
|
"en": {
|
|
"badge": "Clear",
|
|
"label": "Clear",
|
|
"metric": "Clear",
|
|
"stat": "Clear",
|
|
"rendered": "Clear",
|
|
},
|
|
"fr": {
|
|
"badge": "Clair",
|
|
"label": "Clair",
|
|
"metric": "Clair",
|
|
"stat": "Clair",
|
|
"rendered": "Clair",
|
|
},
|
|
"es": {
|
|
"badge": "Transparente",
|
|
"label": "Transparente",
|
|
"metric": "Transparente",
|
|
"stat": "Transparente",
|
|
"rendered": "Transparente",
|
|
},
|
|
"it": {
|
|
"badge": "Chiaro",
|
|
"label": "Chiaro",
|
|
"metric": "Chiaro",
|
|
"stat": "Chiaro",
|
|
"rendered": "Chiaro",
|
|
},
|
|
"pt": {
|
|
"badge": "Clara",
|
|
"label": "Clara",
|
|
"metric": "Investimento claro",
|
|
"stat": "Investimento claro",
|
|
"rendered": "Investimento claro",
|
|
},
|
|
"ru": {
|
|
"badge": "Прозрачно",
|
|
"label": "Прозрачно",
|
|
"metric": "Прозрачно",
|
|
"stat": "Прозрачно",
|
|
"rendered": "Прозрачно",
|
|
},
|
|
},
|
|
},
|
|
"weeks_1_2": {
|
|
"sources": ("1-2 Wochen",),
|
|
"issue_type": "weak_marketing_copy",
|
|
"translations": {
|
|
"fr": "1 à 2 semaines",
|
|
"es": "1-2 semanas",
|
|
"it": "1-2 settimane",
|
|
"pt": "1 a 2 semanas",
|
|
},
|
|
"contexts": {
|
|
"fr": {
|
|
"metric": "1 à 2 semaines",
|
|
"stat": "1 à 2 semaines",
|
|
},
|
|
"es": {
|
|
"metric": "1-2 semanas",
|
|
"stat": "1-2 semanas",
|
|
},
|
|
"it": {
|
|
"metric": "1-2 settimane",
|
|
"stat": "1-2 settimane",
|
|
},
|
|
"pt": {
|
|
"metric": "1 a 2 semanas",
|
|
"stat": "1 a 2 semanas",
|
|
},
|
|
},
|
|
},
|
|
"weeks_2_4": {
|
|
"sources": ("2-4 Wochen",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"fr": "2 à 4 semaines",
|
|
},
|
|
"contexts": {
|
|
"fr": {
|
|
"metric": "2 à 4 semaines",
|
|
"stat": "2 à 4 semaines",
|
|
},
|
|
},
|
|
},
|
|
"days_label": {
|
|
"sources": ("Tages",),
|
|
"issue_type": "weak_marketing_copy",
|
|
"translations": {
|
|
"fr": "jours",
|
|
"pt": "dias",
|
|
},
|
|
},
|
|
"customer_reviews": {
|
|
"sources": ("Kundenschätzung",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"en": "Customer rating",
|
|
"fr": "Avis clients",
|
|
"es": "Valoración de clientes",
|
|
"it": "Valutazione clienti",
|
|
"pt": "Avaliação dos clientes",
|
|
"ru": "Оценка клиентов",
|
|
},
|
|
},
|
|
"editable_label": {
|
|
"sources": ("Bearbeitbar",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"en": "Editable",
|
|
"fr": "Modifiable",
|
|
"es": "Editable",
|
|
"it": "Modificabile",
|
|
"pt": "Editável",
|
|
"ru": "Редактируемо",
|
|
},
|
|
},
|
|
"core_pages_label": {
|
|
"sources": ("Startseite + Kernseiten",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"pt": "Página inicial + páginas essenciais",
|
|
},
|
|
},
|
|
"detailed_page_structure": {
|
|
"sources": ("Detailliertes Seitenlayout",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"fr": "Structure détaillée des pages",
|
|
"es": "Estructura detallada de páginas",
|
|
"it": "Struttura dettagliata delle pagine",
|
|
"pt": "Estrutura detalhada das páginas",
|
|
"ru": "Детальная структура страниц",
|
|
},
|
|
},
|
|
"business_process_cta": {
|
|
"sources": ("Geschäftsprozess besprechen",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"fr": "Échanger sur votre processus métier",
|
|
"es": "Hablar sobre el proceso del negocio",
|
|
"pt": "Falar sobre o processo do negócio",
|
|
},
|
|
},
|
|
"multilingual_rollout": {
|
|
"sources": ("Mehrsprachige Einführung", "Mehrsprachiger Rollout-Plan"),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"fr": "Déploiement multilingue",
|
|
"it": "Lancio multilingue",
|
|
"ru": "Многоязычный запуск",
|
|
},
|
|
},
|
|
"customization_integrations": {
|
|
"sources": ("Anpassung & Integrationen",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"fr": "Personnalisation & intégrations",
|
|
"es": "Personalización e integraciones",
|
|
"it": "Personalizzazioni e integrazioni",
|
|
"pt": "Personalização e integrações",
|
|
"ru": "Настройка и интеграции",
|
|
},
|
|
},
|
|
"transparent_investment": {
|
|
"sources": ("Transparente Investition",),
|
|
"issue_type": "foreign_ui_label",
|
|
"translations": {
|
|
"de": "Transparente Investition",
|
|
"en": "Transparent pricing",
|
|
"fr": "Investissement transparent",
|
|
"es": "Inversión transparente",
|
|
"it": "Investimento trasparente",
|
|
"pt": "Investimento transparente",
|
|
"ru": "Прозрачный бюджет",
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def build_system_vocabulary(locale_code: str, keys: Iterable[str] | None = None) -> dict[str, str]:
|
|
vocabulary: dict[str, str] = {}
|
|
selected_keys = tuple(keys or SYSTEM_STRING_SPECS.keys())
|
|
for key in selected_keys:
|
|
spec = SYSTEM_STRING_SPECS[key]
|
|
target = spec.get("translations", {}).get(locale_code)
|
|
if not target:
|
|
continue
|
|
for source in spec["sources"]:
|
|
vocabulary[source] = target
|
|
return vocabulary
|
|
|
|
|
|
def build_contextual_system_vocabulary(locale_code: str, keys: Iterable[str] | None = None) -> dict[str, dict[str, str]]:
|
|
contextual: dict[str, dict[str, str]] = {}
|
|
selected_keys = tuple(keys or SYSTEM_STRING_SPECS.keys())
|
|
for key in selected_keys:
|
|
spec = SYSTEM_STRING_SPECS[key]
|
|
locale_contexts = spec.get("contexts", {}).get(locale_code, {})
|
|
if not locale_contexts:
|
|
continue
|
|
source = spec["sources"][0]
|
|
for context_name, replacement in locale_contexts.items():
|
|
contextual.setdefault(context_name, {})[source] = replacement
|
|
return contextual
|
|
|
|
|
|
def build_system_rewrite_candidates(keys: Iterable[str] | None = None) -> dict[str, str]:
|
|
candidates: dict[str, str] = {}
|
|
selected_keys = tuple(keys or SYSTEM_STRING_SPECS.keys())
|
|
for key in selected_keys:
|
|
spec = SYSTEM_STRING_SPECS[key]
|
|
for source in spec["sources"]:
|
|
candidates[source] = spec["issue_type"]
|
|
return candidates
|
|
|
|
|
|
def all_system_sources() -> set[str]:
|
|
sources: set[str] = set()
|
|
for spec in SYSTEM_STRING_SPECS.values():
|
|
sources.update(spec["sources"])
|
|
return sources
|
|
|
|
|
|
def is_canonical_system_string(locale_code: str, source: str) -> bool:
|
|
for spec in SYSTEM_STRING_SPECS.values():
|
|
if source in spec.get("canonical_by_locale", {}).get(locale_code, ()):
|
|
return True
|
|
if locale_code == "de":
|
|
return source in all_system_sources()
|
|
replacement = system_string_replacement(locale_code, source)
|
|
return bool(replacement and replacement == source)
|
|
|
|
|
|
def system_string_replacement(locale_code: str, source: str) -> str:
|
|
for spec in SYSTEM_STRING_SPECS.values():
|
|
if source not in spec["sources"]:
|
|
continue
|
|
return spec.get("translations", {}).get(locale_code, "")
|
|
return ""
|