""" Fix script for i18n batch transformation issues: 1. Remove misplaced `const { t } = useTranslation();` lines (inside object literals, etc.) 2. Fix JSX attributes: `=t('...')` -> `={t('...')}` 3. Re-add the hook correctly in the main function component """ import re import os target_dirs = ['src/pages', 'src/components'] skip_files = [ 'src/components/LanguageSwitcher.tsx', 'src/components/Layout.tsx', 'src/i18n/index.ts', 'src/locales/zh/index.ts', 'src/locales/en/index.ts', ] def get_target_files(): files = [] for target_dir in target_dirs: for root, dirs, filenames in os.walk(target_dir): for fname in filenames: if fname.endswith(('.tsx', '.ts', '.jsx', '.js')): filepath = os.path.join(root, fname).replace('\\', '/') if filepath not in skip_files: files.append(filepath) return files target_files = get_target_files() total_fixed = 0 for filepath in target_files: try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read() except: continue original = content fixes = 0 # Step 1: Remove ALL `const { t } = useTranslation();` lines # These might be misplaced (inside object literals, etc.) old_hook_line = '\tconst { t } = useTranslation();\n' old_hook_line2 = '\n\tconst { t } = useTranslation();\n' hook_count = content.count('const { t } = useTranslation();') if hook_count > 0: # Remove all instances content = content.replace(old_hook_line, '') content = content.replace(old_hook_line2, '\n') # Also handle variations content = re.sub(r'\n\s*const \{ t \} = useTranslation\(\);\n', '\n', content) fixes += hook_count # Step 2: Fix JSX attributes: `=t('...')` -> `={t('...')}` # Pattern: word=t('...') where word is a JSX attribute name # But NOT: key: t('...') (JS object property) or return t('...') or = t('...') # JSX attributes look like: placeholder=t('key') or title=t('key') # We need to match: identifier immediately followed by =t(' # Match patterns like: placeholder=t('some.key') or title=t('some.key') # But not patterns like: :t(' or {t(' or (t(' or = t(' (with space) def fix_jsx_attr(match): prefix = match.group(1) key = match.group(2) return f'{prefix}={{t(\'{key}\')}}' # This regex matches: word=t('key') where word is preceded by space or < # and NOT preceded by : or { or ( content = re.sub( r'(? { # or: const ComponentName = (props) => { match = re.search(r'(const \w+\s*=\s*(?:\([^)]*\))?\s*=>\s*\{)', content) if match: insert_pos = match.end() content = content[:insert_pos] + '\n\tconst { t } = useTranslation();' + content[insert_pos:] fixes += 1 else: # Pattern 4: export default () => { match = re.search(r'(export default\s*(?:\([^)]*\))?\s*=>\s*\{)', content) if match: insert_pos = match.end() content = content[:insert_pos] + '\n\tconst { t } = useTranslation();' + content[insert_pos:] fixes += 1 # Step 5: Also check for files that have the import but the hook was removed # Re-add if needed if 'useTranslation' in content and 'import' in content and 'const { t } = useTranslation();' not in content: if has_t_calls or re.search(r"\bt\(['\"]", content): match = re.search(r'(export default function \w+\([^)]*\)\s*\{)', content) if match: insert_pos = match.end() content = content[:insert_pos] + '\n\tconst { t } = useTranslation();' + content[insert_pos:] fixes += 1 else: match = re.search(r'(function \w+\([^)]*\)\s*\{)', content) if match: insert_pos = match.end() content = content[:insert_pos] + '\n\tconst { t } = useTranslation();' + content[insert_pos:] fixes += 1 else: match = re.search(r'(const \w+\s*=\s*(?:\([^)]*\))?\s*=>\s*\{)', content) if match: insert_pos = match.end() content = content[:insert_pos] + '\n\tconst { t } = useTranslation();' + content[insert_pos:] fixes += 1 if content != original: with open(filepath, 'w', encoding='utf-8') as f: f.write(content) total_fixed += 1 print(f" Fixed: {filepath} ({fixes} issues)") print(f"\nDone! Fixed {total_fixed} files")