Add keygen module
Some checks failed
CI / test (3.10) (push) Failing after 14s
CI / test (3.12) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / build (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled

This commit is contained in:
2026-02-02 17:22:18 +00:00
parent 5b1942c333
commit 3bac1a4958

View File

@@ -0,0 +1,163 @@
"""Key generation for translation strings."""
import re
from pathlib import Path
from typing import Dict, List, Optional
class KeyGenerator:
"""Generate translation keys based on conventions."""
def __init__(
self,
style: str = "snake_case",
prefix: Optional[str] = None,
max_length: int = 100,
) -> None:
self.style = style
self.prefix = prefix
self.max_length = max_length
self._existing_keys: Dict[str, int] = {}
def set_existing_keys(self, keys: List[str]) -> None:
"""Set existing translation keys to avoid duplicates."""
self._existing_keys = {}
for key in keys:
if key in self._existing_keys:
self._existing_keys[key] += 1
else:
self._existing_keys[key] = 1
def generate_key(
self,
text: str,
file_path: Optional[Path] = None,
context: Optional[str] = None,
) -> str:
"""Generate a translation key for the given text."""
base_key = self._text_to_key(text)
if file_path:
path_prefix = self._path_to_prefix(file_path)
base_key = f"{path_prefix}_{base_key}"
if context:
context_key = self._text_to_key(context)
base_key = f"{base_key}_{context_key}"
if self.prefix:
base_key = f"{self.prefix}_{base_key}"
unique_key = self._make_unique(base_key)
return unique_key
def _text_to_key(self, text: str) -> str:
"""Convert text to a valid key format."""
normalized = text.lower().strip()
if self.style == "snake_case":
key = re.sub(r"[^a-z0-9]+", "_", normalized)
elif self.style == "kebab-case":
key = re.sub(r"[^a-z0-9]+", "-", normalized)
elif self.style == "camelCase":
key = self._to_camel_case(normalized)
elif self.style == "PascalCase":
key = self._to_pascal_case(normalized)
else:
key = re.sub(r"[^a-z0-9]+", "_", normalized)
key = key.strip("_").strip("-")
key = key[: self.max_length]
return key
def _path_to_prefix(self, file_path: Path) -> str:
"""Convert file path to a key prefix."""
parts = []
for part in file_path.parts:
part = re.sub(r"[^a-z0-9]+", "_", part.lower())
part = part.strip("_")
if part and part not in ("src", "src", "app", "components", "pages", "views"):
parts.append(part)
return "_".join(parts)
def _to_camel_case(self, text: str) -> str:
"""Convert text to camelCase."""
words = re.split(r"[^a-z0-9]+", text.lower())
words = [w for w in words if w]
if not words:
return ""
first = words[0]
rest = [w.capitalize() for w in words[1:]]
return first + "".join(rest)
def _to_pascal_case(self, text: str) -> str:
"""Convert text to PascalCase."""
words = re.split(r"[^a-z0-9]+", text.lower())
words = [w.capitalize() for w in words if w]
return "".join(words)
def _make_unique(self, key: str) -> str:
"""Ensure key is unique by appending a number if needed."""
if key not in self._existing_keys:
self._existing_keys[key] = 0
return key
count = self._existing_keys[key]
self._existing_keys[key] = count + 1
suffix = f"_{count + 1}"
new_key = key + suffix
while new_key in self._existing_keys:
count += 1
suffix = f"_{count}"
new_key = key + suffix
self._existing_keys[new_key] = 0
return new_key
def analyze_existing_keys(translation_file: Path) -> List[str]:
"""Analyze existing translation keys from a file."""
keys: List[str] = []
try:
content = translation_file.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError):
return keys
if translation_file.suffix == ".json":
import json
try:
data = json.loads(content)
if isinstance(data, dict):
keys.extend(_flatten_dict(data))
except json.JSONDecodeError:
pass
elif translation_file.suffix in (".po", ".pot"):
for match in re.finditer(r'^msgid\s+"([^"]+)"', content, re.MULTILINE):
keys.append(match.group(1))
return keys
def _flatten_dict(d: dict, prefix: str = "") -> List[str]:
"""Flatten a nested dictionary to get all keys."""
keys = []
for k, v in d.items():
full_key = f"{prefix}.{k}" if prefix else k
if isinstance(v, dict):
keys.extend(_flatten_dict(v, full_key))
else:
keys.append(full_key)
return keys