diff --git a/i18n_guardian/utils/file_utils.py b/i18n_guardian/utils/file_utils.py new file mode 100644 index 0000000..caf8c01 --- /dev/null +++ b/i18n_guardian/utils/file_utils.py @@ -0,0 +1,67 @@ +"""File utilities.""" + +from pathlib import Path +from typing import List, Optional + + +def read_file_safe(path: Path, encoding: str = "utf-8") -> Optional[str]: + """Safely read a file, returning None on error.""" + try: + return path.read_text(encoding=encoding) + except (OSError, UnicodeDecodeError): + return None + + +def find_translation_files( + path: Path, + patterns: Optional[List[str]] = None, +) -> List[Path]: + """Find translation files in a directory.""" + patterns = patterns or [ + "**/*.json", + "**/*.po", + "**/*.pot", + "**/*.yaml", + "**/*.yml", + ] + + files = [] + for pattern in patterns: + files.extend(path.rglob(pattern)) + + return sorted(set(files)) + + +def is_text_file(path: Path, chunk_size: int = 8192) -> bool: + """Check if a file is a text file.""" + try: + with open(path, "rb") as f: + chunk = f.read(chunk_size) + if b"\x00" in chunk: + return False + return True + except OSError: + return False + + +def backup_file(path: Path) -> Path: + """Create a backup of a file.""" + backup_path = path.with_suffix(path.suffix + ".bak") + if backup_path.exists(): + counter = 1 + while backup_path.exists(): + backup_path = path.with_suffix(f"{path.suffix}.bak{counter}") + counter += 1 + + backup_path.write_bytes(path.read_bytes()) + return backup_path + + +def restore_backup(backup_path: Path, original_path: Path) -> bool: + """Restore a file from backup.""" + try: + original_path.write_bytes(backup_path.read_bytes()) + backup_path.unlink() + return True + except OSError: + return False