优化查找未使用翻译的脚本

This commit is contained in:
Fin 2025-09-05 12:01:15 +08:00
parent 1e340c6578
commit df9e971fba
9 changed files with 24 additions and 246 deletions

View File

@ -140,7 +140,6 @@
06C2CF232685B88D0034B127 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF222685B88D0034B127 /* TextCell.swift */; };
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF242685BDB80034B127 /* SpacerCell.swift */; };
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952C2480E3F8006B98F3 /* LabelCell.swift */; };
06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */; };
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */; };
06C595362481160F006B98F3 /* BKLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C595352481160F006B98F3 /* BKLabel.swift */; };
06CF784721C7A50300A052D7 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@ -380,7 +379,6 @@
06C2CF222685B88D0034B127 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; };
06C2CF242685BDB80034B127 /* SpacerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacerCell.swift; sourceTree = "<group>"; };
06C5952C2480E3F8006B98F3 /* LabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCell.swift; sourceTree = "<group>"; };
06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudStatusCell.swift; sourceTree = "<group>"; };
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingCell.swift; sourceTree = "<group>"; };
06C5953224811505006B98F3 /* ArchiveSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingManager.swift; sourceTree = "<group>"; };
06C595352481160F006B98F3 /* BKLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKLabel.swift; sourceTree = "<group>"; };
@ -505,7 +503,6 @@
0603706820E1F89500F4CA05 /* PreviewCardCell.swift */,
064CAB9D256BE9090018155C /* PreviewCardCellViewModel.swift */,
06C5952C2480E3F8006B98F3 /* LabelCell.swift */,
06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */,
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */,
06BBB8BB2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift */,
060481EF250F51CA00BC9799 /* SoundCell.swift */,
@ -1279,7 +1276,6 @@
064CABA6256BE9510018155C /* PreviewModel.swift in Sources */,
0637FA7E20E0969800E80174 /* Client.swift in Sources */,
0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */,
06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */,
06BBB88725650C6C0076F63E /* ArchiveSettingManager.swift in Sources */,
065BE44B2563D8E1002A8CA4 /* Reusable.swift in Sources */,
0637FA8620E0AB6600E80174 /* UIColor+Extension.swift in Sources */,

View File

@ -291,35 +291,6 @@
}
}
},
"available" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enabled"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "有効"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Etkin"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "已开启"
}
}
}
},
"badge" : {
"extractionState" : "manual",
"localizations" : {
@ -929,35 +900,6 @@
}
}
},
"copyCrashLog" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy Crash Log"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "クラッシュログをコピー"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Copy Crash Log"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制闪退日志"
}
}
}
},
"copyExample" : {
"extractionState" : "manual",
"localizations" : {
@ -1016,64 +958,6 @@
}
}
},
"crashContent" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "You need to restart the app! \n\nIf the problem persists, you can try to check the FAQ to see if there is a solution.\nhttps://bark.day.app/#/en-us/faq\n\nBark will not upload any logs! If you need help (or wish to help the developer fix the crash), you can also send the crash log to me, and I will contact you as soon as possible.\n\nEmail: to@day.app\nTelegram: https://t.me/joinchat/OsCbLzovUAE0YjY1\nGithub Issue: https://github.com/Finb/Bark/issues\n"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "問題が解決しない場合は、FAQを確認して解決策があるかどうかを確認できます。 \nhttps://bark.day.app/#/en-us/faq \n\nBarkはログをアップロードしません助けが必要な場合または開発者がクラッシュを修正するのを手伝いたい場合、クラッシュログを私に送ってください。できるだけ早く連絡します。 \n\nメール: to@day.app \nTelegram: https://t.me/joinchat/OsCbLzovUAE0YjY1 \nGithub Issue: https://github.com/Finb/Bark/issues "
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Uygulamayı yeniden başlatmanız gerekiyor!\n\nSorun devam ederse bir çözüm olup olmadığını görmek için SSS'yi kontrol etmeyi deneyebilirsiniz.\nhttps://bark.day.app/#/en-us/faq\n\nBark herhangi bir günlük yüklemeyecek! Yardıma ihtiyacınız varsa (veya geliştiricinin çökmeyi düzeltmesine yardım etmek istiyorsanız), kilitlenme günlüğünü bana da gönderebilirsiniz; mümkün olan en kısa sürede sizinle iletişime geçeceğim.\n\nE-posta: to@day.app\nTelgraf: https://t.me/joinchat/OsCbLzovUAE0YjY1\nGithub Sorunları: https://github.com/Finb/Bark/issues"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "您需要重启APP\n\n如还是提示闪退可以尝试查看FAQ看是否有解决办法\nhttps://bark.day.app/#/faq\n\nBark 不会上传任何日志!如您需帮助(或帮助作者修复闪退),可以将闪退日志手动复制发送给我,我会尽快与您联系。\n\n邮箱: to@day.app\nTelegram: https://t.me/joinchat/OsCbLzov\nGithub Issue: https://github.com/Finb/Bark/issues\n"
}
}
}
},
"crashed" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Crashed!"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "クラッシュしました!"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "düştü!"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "好像闪退了!"
}
}
}
},
"criticalAlert" : {
"extractionState" : "manual",
"localizations" : {
@ -2001,35 +1885,6 @@
}
}
},
"iCloudSatatus" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud Sync"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud同期"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud Senkronizasyonu"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "iCloud 同步"
}
}
}
},
"imageParameter" : {
"extractionState" : "manual",
"localizations" : {
@ -3132,35 +2987,6 @@
}
}
},
"restricted" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Disabled"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "無効"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Devre Dışı"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "未开启"
}
}
}
},
"ringtone" : {
"extractionState" : "manual",
"localizations" : {

View File

@ -58,7 +58,7 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
btn.setImage(UIImage(named: "group_collapse")?.withRenderingMode(.alwaysTemplate), for: .selected)
btn.imageView?.tintColor = BKColor.black
btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
btn.accessibilityLabel = "toggle"
btn.accessibilityLabel = "toggle".localized
return UIBarButtonItem(customView: btn)
}()

View File

@ -21,7 +21,6 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
tableView.separatorColor = BKColor.grey.lighten3
tableView.backgroundColor = BKColor.background.primary
tableView.register(LabelCell.self, forCellReuseIdentifier: "\(LabelCell.self)")
tableView.register(iCloudStatusCell.self, forCellReuseIdentifier: "\(iCloudStatusCell.self)")
tableView.register(ArchiveSettingCell.self, forCellReuseIdentifier: "\(ArchiveSettingCell.self)")
tableView.register(DetailTextCell.self, forCellReuseIdentifier: "\(DetailTextCell.self)")
tableView.register(MutableTextCell.self, forCellReuseIdentifier: "\(MutableTextCell.self)")
@ -178,10 +177,6 @@ class MessageSettingsViewController: BaseViewController<MessageSettingsViewModel
cell.textLabel?.text = text
return cell
}
case .iCloudStatus:
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") {
return cell
}
case .backup(let viewModel):
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(MutableTextCell.self)") as? MutableTextCell {
cell.textLabel?.textColor = BKColor.blue.darken1

View File

@ -227,8 +227,6 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
enum MessageSettingItem {
//
case label(text: String)
// iCloud
case iCloudStatus
//
case archiveSetting(viewModel: ArchiveSettingCellViewModel)
// cell

View File

@ -18,7 +18,7 @@ class PreviewCardCell: BaseTableViewCell<PreviewCardCellViewModel> {
let copyButton: IconButton = {
let button = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: BKColor.grey.base)
button.accessibilityLabel = "copy".localized
button.accessibilityLabel = "Copy".localized
return button
}()

View File

@ -13,7 +13,7 @@ import UIKit
class SoundCell: BaseTableViewCell<SoundCellViewModel> {
let copyButton: IconButton = {
let button = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: BKColor.grey.base)
button.accessibilityLabel = "copy".localized
button.accessibilityLabel = "Copy".localized
return button
}()

View File

@ -1,40 +0,0 @@
//
// iCloudStatusCell.swift
// Bark
//
// Created by huangfeng on 2020/5/29.
// Copyright © 2020 Fin. All rights reserved.
//
import CloudKit
import UIKit
class iCloudStatusCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .value1, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
self.backgroundColor = BKColor.background.secondary
self.textLabel?.text = "iCloudStatus".localized
self.detailTextLabel?.text = ""
self.detailTextLabel?.textColor = BKColor.grey.darken2
CKContainer.default().accountStatus { status, _ in
dispatch_sync_safely_main_queue {
switch status {
case .available:
self.detailTextLabel?.text = "available".localized
case .noAccount, .restricted, .temporarilyUnavailable:
self.detailTextLabel?.text = "restricted".localized
case .couldNotDetermine:
self.detailTextLabel?.text = "unknown".localized
@unknown default:
break
}
}
}
}
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -3,7 +3,9 @@
Bark项目本地化字符串分析工具
这个脚本会扫描整个Bark项目找出 Localizable.xcstrings 中未使用的翻译key
检测方式任何在双引号内且在本地化文件中定义的字符串都会被认为是被使用的key
检测方式
1. 任何在双引号内且在本地化文件中定义的字符串都会被认为是被使用的key
2. "key".localized "key".localized(with:) 模式
使用方法:
python3 check_unused_translations.py
@ -45,7 +47,7 @@ class BarkLocalizationAnalyzer:
def extract_used_keys_from_file(self, file_path, all_defined_keys):
"""从Swift文件中提取使用的本地化key"""
used_keys = set()
nslocalizedstring_keys = set() # 新增专门收集NSLocalizedString中的key
localized_keys = set() # 收集.localized中的key
try:
with open(file_path, 'r', encoding='utf-8') as f:
@ -61,26 +63,27 @@ class BarkLocalizationAnalyzer:
if quoted_string and quoted_string in all_defined_keys:
used_keys.add(quoted_string)
# 方法2: 专门查找 NSLocalizedString("key") 模式
nslocalizedstring_patterns = [
r'NSLocalizedString\s*\(\s*"([^"]+)"\s*\)', # NSLocalizedString("key")
r'NSLocalizedString\s*\(\s*\'([^\']+)\'\s*\)', # NSLocalizedString('key')
r'NSLocalizedString\s*\(\s*@"([^"]+)"\s*\)', # NSLocalizedString(@"key")
# 方法2: 查找 "key".localized 和 "key".localized(with:) 模式
localized_patterns = [
r'"([^"]+)"\s*\.\s*localized\b', # "key".localized
r'"([^"]+)"\s*\.\s*localized\s*\(\s*with:', # "key".localized(with:
r'\'([^\']+)\'\s*\.\s*localized\b', # 'key'.localized
r'\'([^\']+)\'\s*\.\s*localized\s*\(\s*with:', # 'key'.localized(with:
]
for pattern in nslocalizedstring_patterns:
for pattern in localized_patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL)
for match in matches:
match = match.strip()
if match:
nslocalizedstring_keys.add(match) # 收集所有NSLocalizedString中的key
localized_keys.add(match) # 收集所有.localized中的key
if match in all_defined_keys:
used_keys.add(match)
except Exception as e:
print(f"⚠️ 读取文件失败 {file_path}: {e}")
return used_keys, nslocalizedstring_keys
return used_keys, localized_keys
def find_all_used_keys(self, all_defined_keys):
"""在整个项目中查找所有使用的本地化 key"""
@ -90,20 +93,20 @@ class BarkLocalizationAnalyzer:
# 提取使用的key
used_keys = set()
all_nslocalizedstring_keys = set() # 新增收集所有NSLocalizedString中的key
all_localized_keys = set() # 收集所有.localized中的key
files_with_keys = 0
for file_path in swift_files:
file_keys, nsl_keys = self.extract_used_keys_from_file(file_path, all_defined_keys)
file_keys, localized_keys = self.extract_used_keys_from_file(file_path, all_defined_keys)
if file_keys:
files_with_keys += 1
used_keys.update(file_keys)
all_nslocalizedstring_keys.update(nsl_keys)
all_localized_keys.update(localized_keys)
print(f"🔑 在 {files_with_keys} 个文件中找到 {len(used_keys)} 个使用的key")
# 计算在NSLocalizedString中使用但未在本地化文件中定义的key
missing_in_localization = all_nslocalizedstring_keys - all_defined_keys
# 计算在代码中使用但未在本地化文件中定义的key
missing_in_localization = all_localized_keys - all_defined_keys
return used_keys, files_with_keys, missing_in_localization
@ -160,7 +163,7 @@ class BarkLocalizationAnalyzer:
print(f"使用中的key数量: {result['used_keys']}")
print(f"未使用的key数量: {result['unused_keys']}")
print(f"缺失的key数量: {result['missing_keys']} (代码中使用但未定义)")
print(f"NSLocalizedString中缺失的key: {result['missing_in_localization']}")
print(f"代码中缺失的key: {result['missing_in_localization']}")
if result['unused_keys_list']:
print(f"\n🗑️ 未使用的翻译key ({result['unused_keys']} 个):")
@ -173,7 +176,7 @@ class BarkLocalizationAnalyzer:
print(f" {i:2d}. {key}")
if result['missing_in_localization_list']:
print(f"\nNSLocalizedString中使用但未在Localizable.xcstrings中定义的key ({result['missing_in_localization']} 个):")
print(f"\n代码中使用但未在Localizable.xcstrings中定义的key ({result['missing_in_localization']} 个):")
for i, key in enumerate(result['missing_in_localization_list'], 1):
print(f" {i:2d}. {key}")
@ -210,7 +213,7 @@ def main():
if result['missing_keys'] > 0:
print(f" - 为 {result['missing_keys']} 个缺失的key添加翻译")
if result['missing_in_localization'] > 0:
print(f" - 为 {result['missing_in_localization']}NSLocalizedString中的key添加本地化定义")
print(f" - 为 {result['missing_in_localization']}代码中使用的key添加本地化定义")
print(" - 检查是否有动态构建的key名称(脚本可能无法检测)")
print(" - 手动检查Storyboard/XIB文件中的硬编码字符串")