Files
7000pctAUTO c358d56842
Some checks failed
CI / test (push) Has been cancelled
Add source files
2026-02-02 03:56:42 +00:00

223 lines
6.2 KiB
Python

"""Locale file parsing for JSON and YAML format."""
import json
from pathlib import Path
from typing import Any, Dict, List, Optional, Set
class LocaleParser:
"""Parse and manage locale files in JSON and YAML formats."""
def __init__(self):
"""Initialize the locale parser."""
self._cache: Dict[str, Dict[str, Any]] = {}
def parse_file(self, file_path: Path | str) -> Dict[str, Any]:
"""Parse a single locale file.
Args:
file_path: Path to the locale file.
Returns:
Dictionary of locale keys and values.
Raises:
ValueError: If the file format is not supported.
FileNotFoundError: If the file doesn't exist.
"""
path = Path(file_path)
path_str = str(path)
if path_str in self._cache:
return self._cache[path_str]
content = path.read_text(encoding="utf-8")
suffix = path.suffix.lower()
if suffix == ".json":
data = json.loads(content)
elif suffix in [".yaml", ".yml"]:
import yaml
data = yaml.safe_load(content)
else:
raise ValueError(f"Unsupported locale file format: {suffix}")
if not isinstance(data, dict):
data = {}
self._cache[str(file_path)] = data
return data
def get_all_keys(self, data: Dict[str, Any], prefix: str = "") -> Set[str]:
"""Get all keys from nested dictionary, including nested keys as dot notation.
Args:
data: Nested dictionary of locale data.
prefix: Prefix for nested keys.
Returns:
Set of all keys in dot notation.
"""
keys = set()
for key, value in data.items():
full_key = f"{prefix}.{key}" if prefix else key
if isinstance(value, dict):
keys.update(self.get_all_keys(value, full_key))
else:
keys.add(full_key)
return keys
def flatten_keys(self, data: Dict[str, Any], prefix: str = "") -> Dict[str, Any]:
"""Flatten nested dictionary to dot notation.
Args:
data: Nested dictionary of locale data.
prefix: Prefix for nested keys.
Returns:
Flattened dictionary with dot notation keys.
"""
flattened = {}
for key, value in data.items():
full_key = f"{prefix}.{key}" if prefix else key
if isinstance(value, dict):
flattened.update(self.flatten_keys(value, full_key))
else:
flattened[full_key] = value
return flattened
def unflatten_keys(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Unflatten dot notation keys to nested dictionary.
Args:
data: Flattened dictionary with dot notation keys.
Returns:
Nested dictionary.
"""
nested: Dict[str, Any] = {}
for key, value in data.items():
parts = key.split(".")
current = nested
for part in parts[:-1]:
if part not in current:
current[part] = {}
current = current[part]
current[parts[-1]] = value
return nested
def parse_directory(
self,
dir_path: str,
locales: Optional[List[str]] = None,
) -> Dict[str, Set[str]]:
"""Parse all locale files in a directory.
Args:
dir_path: Path to the locale directory.
locales: Optional list of locale names to include.
Returns:
Dictionary mapping locale names to their key sets.
"""
locale_dir = Path(dir_path)
results: Dict[str, Set[str]] = {}
if not locale_dir.exists():
return results
for locale_file in locale_dir.iterdir():
if not locale_file.is_file():
continue
locale_name = locale_file.stem
if locales and locale_name not in locales:
continue
try:
data = self.parse_file(locale_file)
keys = self.get_all_keys(data)
results[locale_name] = keys
except (ValueError, json.JSONDecodeError) as e:
if locales:
raise ValueError(f"Error parsing {locale_file}: {e}")
return results
def write_file(
self,
file_path: Path,
data: Dict[str, Any],
format: str = "json",
) -> None:
"""Write locale data to a file.
Args:
file_path: Path to the output file.
data: Dictionary of locale data.
format: Output format (json or yaml).
"""
content: str
if format == "json":
content = json.dumps(data, indent=2, ensure_ascii=False)
elif format in ["yaml", "yml"]:
import yaml
content = yaml.dump(data, default_flow_style=False, allow_unicode=True)
else:
raise ValueError(f"Unsupported format: {format}")
file_path.write_text(content, encoding="utf-8")
def add_key(
self,
data: Dict[str, Any],
key: str,
value: Any,
) -> Dict[str, Any]:
"""Add a key to locale data, creating nested structure if needed.
Args:
data: Existing locale data.
key: Key in dot notation.
value: Value to set.
Returns:
Updated locale data.
"""
parts = key.split(".")
current = data
for part in parts[:-1]:
if part not in current:
current[part] = {}
current = current[part]
current[parts[-1]] = value
return data
def get_value(self, data: Dict[str, Any], key: str) -> Optional[Any]:
"""Get a value from locale data using dot notation.
Args:
data: Locale data dictionary.
key: Key in dot notation.
Returns:
The value if found, None otherwise.
"""
parts = key.split(".")
current = data
for part in parts:
if isinstance(current, dict) and part in current:
current = current[part]
else:
return None
return current