Skip to content

Commit 1cbffb7

Browse files
committed
ci: detect unused ids and deprecated ids still used in the codebase
1 parent 35dbc72 commit 1cbffb7

File tree

2 files changed

+113
-1
lines changed

2 files changed

+113
-1
lines changed

.gitlab-ci.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,19 @@ test_format:
2929
stage: format
3030
extends: .setup_env
3131
tags:
32-
- osx-m4
32+
- mini
3333
script:
3434
- fastlane run swiftlint
3535

36+
check_localization:
37+
stage: format
38+
extends: .setup_env
39+
needs: [] # Run in parallel with other format jobs
40+
tags:
41+
- mini
42+
script:
43+
- python3 tools/check_localization.py
44+
3645
# BUILD
3746
build_strings:
3847
stage: build

tools/check_localization.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import re
4+
import sys
5+
from typing import Set, Dict
6+
7+
def extract_string_ids_from_code(root_dir: str) -> Set[str]:
8+
"""Extract all localized string IDs from Swift source files."""
9+
string_ids = set()
10+
swift_files_pattern = re.compile(r'.*\.swift$')
11+
localization_pattern = re.compile(r'["\'](.*?)["\']\.localized')
12+
format_pattern = re.compile(r'["\']([^"\']*?)["\']\.localizedFormat')
13+
14+
for root, _, files in os.walk(root_dir):
15+
for file in files:
16+
if swift_files_pattern.match(file):
17+
file_path = os.path.join(root, file)
18+
with open(file_path, 'r', encoding='utf-8') as f:
19+
content = f.read()
20+
# Find .localized usage
21+
matches = localization_pattern.findall(content)
22+
string_ids.update(matches)
23+
# Find .localizedFormat usage
24+
matches = format_pattern.findall(content)
25+
string_ids.update(matches)
26+
27+
return string_ids
28+
29+
def extract_string_ids_from_strings_file(strings_file: str) -> Set[str]:
30+
"""Extract all string IDs from a .strings file."""
31+
string_ids = set()
32+
# Match "string_id" = "translation";
33+
pattern = re.compile(r'^"(.*?)"\s*=\s*".*?";$')
34+
35+
with open(strings_file, 'r', encoding='utf-8') as f:
36+
for line in f:
37+
line = line.strip()
38+
match = pattern.match(line)
39+
if match:
40+
string_ids.add(match.group(1))
41+
42+
return string_ids
43+
44+
def main():
45+
# Get source directory (assuming script is in tools/)
46+
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
47+
48+
# Extract string IDs from code
49+
used_ids = extract_string_ids_from_code(os.path.join(root_dir, 'gaios'))
50+
51+
# Extract string IDs from English strings file (source of truth)
52+
en_strings_file = os.path.join(root_dir, 'gaios', 'en.lproj', 'Localizable.strings')
53+
defined_ids = extract_string_ids_from_strings_file(en_strings_file)
54+
55+
# Find deprecated IDs (in strings file but not in code)
56+
deprecated_ids = defined_ids - used_ids
57+
58+
# Find missing IDs (in code but not in strings file)
59+
missing_ids = used_ids - defined_ids
60+
61+
# Separate missing IDs from strings that need localization
62+
actual_missing_ids = {id for id in missing_ids if id.startswith('id_')}
63+
needs_localization = missing_ids - actual_missing_ids
64+
65+
has_issues = False
66+
67+
print("\n" + "="*80)
68+
print("LOCALIZATION CHECK RESULTS")
69+
print("="*80)
70+
71+
if deprecated_ids:
72+
print("\n\n" + "="*50)
73+
print("\n📝 DEPRECATED IDs (in strings file but not used in code):")
74+
print("=" * 50 + "\n\n")
75+
for id in sorted(deprecated_ids):
76+
print(f" - {id}")
77+
78+
if actual_missing_ids:
79+
has_issues = True
80+
print("\n\n" + "="*50)
81+
print("\n❌ MISSING IDs (used in code but missing from Localizable.strings):")
82+
print("=" * 50 + "\n\n")
83+
for id in sorted(actual_missing_ids):
84+
print(f" - {id}")
85+
86+
if needs_localization:
87+
print("\n\n" + "="*50)
88+
print("\n⚠️ STRINGS THAT NEED LOCALIZATION (hardcoded strings using .localized):")
89+
print("=" * 50 + "\n\n")
90+
for text in sorted(needs_localization):
91+
print(f" - {text}")
92+
93+
print("\n" + "="*80)
94+
95+
if has_issues:
96+
# Exit with error only if there are actual missing IDs
97+
sys.exit(1)
98+
99+
print("✅ No critical localization issues found!")
100+
sys.exit(0)
101+
102+
if __name__ == '__main__':
103+
main()

0 commit comments

Comments
 (0)