Skip to content

Instantly share code, notes, and snippets.

@flodolo
Created May 12, 2025 09:19
Show Gist options
  • Save flodolo/de2eb7bc74888c5aa556af8a3a6bf3e3 to your computer and use it in GitHub Desktop.
Save flodolo/de2eb7bc74888c5aa556af8a3a6bf3e3 to your computer and use it in GitHub Desktop.
Identify strings in en-US .strings files but not available in Strings.swift
#!/usr/bin/env python3
"""
Script to find unused localized string IDs in .strings files under en-US.lproj
folders by checking their definitions in firefox-ios/Shared/Strings.swift.
"""
import re
import sys
from pathlib import Path
def find_strings_files(root: Path):
"""Recursively find all .strings files in en-US.lproj directories."""
search_path = root / "firefox-ios"
return search_path.rglob("en-US.lproj/*.strings")
def extract_keys_from_strings_file(strings_path: Path, root: Path):
"""Extract all keys from a .strings file."""
pattern = re.compile(r'^\s*"([^"]+)"\s*=')
keys = []
strings_rel_path = strings_path.relative_to(root)
with strings_path.open("r", encoding="utf-8") as f:
for line in f:
match = pattern.match(line)
if match:
keys.append(f"{strings_rel_path}:{match.group(1)}")
return keys
def extract_defined_keys_from_swift(swift_path: Path):
"""Extract all keys defined in Strings.swift via MZLocalizedString calls."""
pattern = re.compile(r'key:\s*"([^"]+)"')
content = swift_path.read_text(encoding="utf-8")
return pattern.findall(content)
# List of string IDs to exclude from unused report
excluded_ids = set(
[
"firefox-ios/Client/en-US.lproj/InfoPlist.strings:NSCameraUsageDescription",
"firefox-ios/Client/en-US.lproj/InfoPlist.strings:NSFaceIDUsageDescription",
"firefox-ios/Client/en-US.lproj/InfoPlist.strings:NSLocationWhenInUseUsageDescription",
"firefox-ios/Client/en-US.lproj/InfoPlist.strings:NSMicrophoneUsageDescription",
"firefox-ios/Client/en-US.lproj/InfoPlist.strings:NSPhotoLibraryAddUsageDescription",
"firefox-ios/Client/en-US.lproj/InfoPlist.strings:New Private Tab",
"firefox-ios/Client/en-US.lproj/InfoPlist.strings:Scan QR Code",
"firefox-ios/Shared/en-US.lproj/Default Browser.strings:DefaultBrowserCard.Button.v2",
"firefox-ios/Shared/en-US.lproj/Localizable.strings:A username and password are being requested by %@. The site says: %@",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:2GqvPe",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:PzSrmZ-2GqvPe",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:PzSrmZ-eHmH1H",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:PzSrmZ-scEmjs",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:PzSrmZ-xRJbBP",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:ctDNmu",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:eHmH1H",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:eV8mOT",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:eqyNJg",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:fi3W24-2GqvPe",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:fi3W24-eHmH1H",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:fi3W24-scEmjs",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:fi3W24-xRJbBP",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:scEmjs",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:w9jdPK",
"firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings:xRJbBP",
]
)
def main(root_dir: Path):
# Locate Strings.swift
swift_file = root_dir / "firefox-ios" / "Shared" / "Strings.swift"
if not swift_file.exists():
print(f"Error: Strings.swift not found at {swift_file}", file=sys.stderr)
sys.exit(1)
# Gather all keys from .strings files
all_keys = set()
for strings_file in find_strings_files(root_dir):
all_keys.update(extract_keys_from_strings_file(strings_file, root_dir))
# Gather all keys defined in Strings.swift
defined_keys = set(extract_defined_keys_from_swift(swift_file))
unused_keys = set()
# Compute unused keys
for key in all_keys:
id = key.split(":")[1]
if key in excluded_ids:
continue
if id not in defined_keys:
unused_keys.add(key)
unused_keys = sorted(unused_keys)
if unused_keys:
for key in unused_keys:
print(f"Unused string ID: {key}")
else:
print("No unused string IDs found.")
if __name__ == "__main__":
root = (
Path(sys.argv[1])
if len(sys.argv) > 1
else sys.exit("Provide the path to the Firefox iOS repo.")
)
main(root)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment