diff --git a/StikJIT/Resources/Localizable.xcstrings b/StikJIT/Resources/Localizable.xcstrings new file mode 100644 index 0000000..64eff22 --- /dev/null +++ b/StikJIT/Resources/Localizable.xcstrings @@ -0,0 +1,1774 @@ +{ + "sourceLanguage" : "zh-Hans", + "strings" : { + "Apps" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apps" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "应用" + } + } + } + }, + "Scripts" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scripts" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "脚本" + } + } + } + }, + "Tools" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tools" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "工具" + } + } + } + }, + "News" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "News" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "资讯" + } + } + } + }, + "Console" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Console" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "控制台" + } + } + } + }, + "Device Info" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device Info" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备信息" + } + } + } + }, + "App Expiry" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Expiry" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "应用过期" + } + } + } + }, + "Processes" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Processes" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "进程" + } + } + } + }, + "Location" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Location" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "定位" + } + } + } + }, + "Settings" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设置" + } + } + } + }, + "Manage installed apps" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manage installed apps" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "管理已安装的应用" + } + } + } + }, + "Manage and run JS scripts" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manage and run JS scripts" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "管理和运行 JS 脚本" + } + } + } + }, + "Access additional tools" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Access additional tools" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "访问更多工具" + } + } + } + }, + "Latest StikDebug updates" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latest StikDebug updates" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "StikDebug 最新动态" + } + } + } + }, + "Live device logs" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Live device logs" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "实时设备日志" + } + } + } + }, + "View detailed device metadata" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "View detailed device metadata" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "查看设备详细信息" + } + } + } + }, + "Check app expiration dates" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Check app expiration dates" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "查看应用过期日期" + } + } + } + }, + "Inspect running apps" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inspect running apps" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "查看运行中的应用" + } + } + } + }, + "Simulate GPS location" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Simulate GPS location" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "模拟 GPS 定位" + } + } + } + }, + "Configure StikDebug" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure StikDebug" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "配置 StikDebug" + } + } + } + }, + "Location Simulation" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Location Simulation" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "位置模拟" + } + } + } + }, + "Star on GitHub" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Star on GitHub" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "在 GitHub 上标星" + } + } + } + }, + "Pairing File" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pairing File" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "配对文件" + } + } + } + }, + "Import Pairing File" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import Pairing File" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "导入配对文件" + } + } + } + }, + "Importing pairing file…" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importing pairing file…" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "正在导入配对文件…" + } + } + } + }, + "Silent Audio" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Silent Audio" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "静音音频" + } + } + } + }, + "Plays inaudible audio so iOS keeps the app running." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plays inaudible audio so iOS keeps the app running." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "播放无声音频,使 iOS 保持应用运行。" + } + } + } + }, + "Background Location" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Background Location" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "后台定位" + } + } + } + }, + "Uses low-accuracy location to stay alive when an activity needs it." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uses low-accuracy location to stay alive when an activity needs it." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用低精度定位,在活动需要时保持应用存活。" + } + } + } + }, + "Background Keep-Alive" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Background Keep-Alive" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "后台保活" + } + } + } + }, + "Always Run Scripts" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always Run Scripts" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "始终运行脚本" + } + } + } + }, + "Treats device as TXM-capable to bypass hardware checks." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Treats device as TXM-capable to bypass hardware checks." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "将设备视为支持 TXM,绕过硬件检查。" + } + } + } + }, + "Behavior" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Behavior" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "行为" + } + } + } + }, + "Advanced" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advanced" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高级" + } + } + } + }, + "Target Device IP" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Target Device IP" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标设备 IP" + } + } + } + }, + "App Folder" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App Folder" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "应用文件夹" + } + } + } + }, + "Redownload DDI" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Redownload DDI" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "重新下载 DDI" + } + } + } + }, + "Help" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Help" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "帮助" + } + } + } + }, + "Pairing File Guide" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pairing File Guide" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "配对文件指南" + } + } + } + }, + "Download LocalDevVPN" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download LocalDevVPN" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "下载 LocalDevVPN" + } + } + } + }, + "Discord Support" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discord Support" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Discord 支持" + } + } + } + }, + "Redownload DDI Files?" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Redownload DDI Files?" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "重新下载 DDI 文件?" + } + } + } + }, + "Redownload" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Redownload" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "重新下载" + } + } + } + }, + "Cancel" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消" + } + } + } + }, + "Existing DDI files will be removed before downloading fresh copies." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Existing DDI files will be removed before downloading fresh copies." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "现有的 DDI 文件将被删除,然后重新下载。" + } + } + } + }, + "Preparing download…" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preparing download…" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "准备下载…" + } + } + } + }, + "DDI files refreshed successfully." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DDI files refreshed successfully." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "DDI 文件刷新成功。" + } + } + } + }, + "Failed to redownload DDI files: %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Failed to redownload DDI files: %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "DDI 文件重新下载失败:%@" + } + } + } + }, + "Language" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Language" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "语言" + } + } + } + }, + "Enable JIT" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable JIT" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用 JIT" + } + } + } + }, + "Launch Apps" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch Apps" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启动应用" + } + } + } + }, + "Search apps or bundle ID" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search apps or bundle ID" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "搜索应用或 Bundle ID" + } + } + } + }, + "Search" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "搜索" + } + } + } + }, + "JIT" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "JIT" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "JIT" + } + } + } + }, + "Other" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Other" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "其他" + } + } + } + }, + "Done" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Done" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "完成" + } + } + } + }, + "No matching apps" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No matching apps" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "没有匹配的应用" + } + } + } + }, + "No JIT Apps Found" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No JIT Apps Found" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "未找到 JIT 应用" + } + } + } + }, + "Try a different name or bundle identifier." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Try a different name or bundle identifier." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "请尝试其他名称或 Bundle ID。" + } + } + } + }, + "StikDebug can only connect to apps with the \"get-task-allow\" entitlement." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "StikDebug can only connect to apps with the \"get-task-allow\" entitlement." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "StikDebug 只能连接具有 \"get-task-allow\" 权限的应用。" + } + } + } + }, + "Favorites (%d/4)" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorites (%d/4)" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收藏 (%d/4)" + } + } + } + }, + "Recents" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recents" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "最近使用" + } + } + } + }, + "Apps with get-task-allow" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apps with get-task-allow" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "具有 get-task-allow 的应用" + } + } + } + }, + "No matches" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No matches" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "没有匹配项" + } + } + } + }, + "No Apps Found" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Apps Found" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "未找到应用" + } + } + } + }, + "Try another name or bundle identifier." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Try another name or bundle identifier." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "请尝试其他名称或 Bundle ID。" + } + } + } + }, + "Once your device pairing file is imported and CoreDevice is connected, all apps will appear here." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Once your device pairing file is imported and CoreDevice is connected, all apps will appear here." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "导入设备配对文件并连接 CoreDevice 后,所有应用将显示在此处。" + } + } + } + }, + "All Apps" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "All Apps" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "所有应用" + } + } + } + }, + "Remove from Home" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove from Home" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "从主页移除" + } + } + } + }, + "Add to Home" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add to Home" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加到主页" + } + } + } + }, + "Copy Bundle ID" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy Bundle ID" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "复制 Bundle ID" + } + } + } + }, + "Unpin" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unpin" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消固定" + } + } + } + }, + "Pin" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "固定" + } + } + } + }, + "Remove Favorite" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove Favorite" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消收藏" + } + } + } + }, + "Add to Favorites" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add to Favorites" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加到收藏" + } + } + } + }, + "Assign Script" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Assign Script" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "分配脚本" + } + } + } + }, + "Reset Script" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reset Script" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "重置脚本" + } + } + } + }, + "Unfavorite" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unfavorite" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消收藏" + } + } + } + }, + "Favorite" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorite" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收藏" + } + } + } + }, + "Copy ID" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy ID" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "复制 ID" + } + } + } + }, + "Launching %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launching %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "正在启动 %@" + } + } + } + }, + "Launch request sent for %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch request sent for %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已发送启动请求:%@" + } + } + } + }, + "Launch failed for %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch failed for %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启动失败:%@" + } + } + } + }, + "Enable JIT for %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enable JIT for %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "为 %@ 启用 JIT" + } + } + } + }, + "Double-tap to open the app and enable JIT. Use the actions rotor for favorites or bundle ID." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Double-tap to open the app and enable JIT. Use the actions rotor for favorites or bundle ID." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "双击打开应用并启用 JIT。使用操作转轮查看收藏或 Bundle ID。" + } + } + } + }, + "Bundle ID %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bundle ID %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bundle ID %@" + } + } + } + }, + "Assigned script %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Assigned script %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已分配脚本 %@" + } + } + } + }, + "Remove from Favorites" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove from Favorites" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "从收藏中移除" + } + } + } + }, + "Favorites are full" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorites are full" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收藏已满" + } + } + } + }, + "Removed from Favorites" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Removed from Favorites" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已从收藏中移除" + } + } + } + }, + "Added to Favorites" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Added to Favorites" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已添加到收藏" + } + } + } + }, + "Bundle ID copied" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bundle ID copied" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bundle ID 已复制" + } + } + } + }, + "Launch" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启动" + } + } + } + }, + "Launch %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启动 %@" + } + } + } + }, + "Launch request in progress." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch request in progress." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启动请求进行中。" + } + } + } + }, + "Double-tap to launch this app without enabling JIT." : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Double-tap to launch this app without enabling JIT." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "双击启动此应用(不启用 JIT)。" + } + } + } + }, + "Launch App" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch App" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启动应用" + } + } + } + }, + "Launching" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launching" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启动中" + } + } + } + }, + "Ready" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ready" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "就绪" + } + } + } + }, + "Close" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关闭" + } + } + } + }, + "OK" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "确定" + } + } + } + }, + "Try Again" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Try Again" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "重试" + } + } + } + }, + "Imported successfully" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Imported successfully" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "导入成功" + } + } + } + }, + "Import failed: %@" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Import failed: %@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "导入失败:%@" + } + } + } + } + }, + "version" : "1.0" +} diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index c3c880a..d817944 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -159,6 +159,8 @@ struct HeartbeatApp: App { var body: some Scene { WindowGroup { MainTabView() + .environment(LanguageManager.shared) + .id(LanguageManager.shared.language) .onAppear { Task { let fileManager = FileManager.default diff --git a/StikJIT/Utilities/AppFeature.swift b/StikJIT/Utilities/AppFeature.swift index 4395861..06bfa5d 100644 --- a/StikJIT/Utilities/AppFeature.swift +++ b/StikJIT/Utilities/AppFeature.swift @@ -24,57 +24,57 @@ enum AppFeature: String, CaseIterable, Identifiable { var title: String { switch self { case .home: - return "Apps" + return "Apps".localized case .scripts: - return "Scripts" + return "Scripts".localized case .tools: - return "Tools" + return "Tools".localized case .news: - return "News" + return "News".localized case .console: - return "Console" + return "Console".localized case .deviceInfo: - return "Device Info" + return "Device Info".localized case .profiles: - return "App Expiry" + return "App Expiry".localized case .processes: - return "Processes" + return "Processes".localized case .location: - return "Location" + return "Location".localized case .settings: - return "Settings" + return "Settings".localized } } var detail: String { switch self { case .home: - return "Manage installed apps" + return "Manage installed apps".localized case .scripts: - return "Manage and run JS scripts" + return "Manage and run JS scripts".localized case .tools: - return "Access additional tools" + return "Access additional tools".localized case .news: - return "Latest StikDebug updates" + return "Latest StikDebug updates".localized case .console: - return "Live device logs" + return "Live device logs".localized case .deviceInfo: - return "View detailed device metadata" + return "View detailed device metadata".localized case .profiles: - return "Check app expiration dates" + return "Check app expiration dates".localized case .processes: - return "Inspect running apps" + return "Inspect running apps".localized case .location: - return "Simulate GPS location" + return "Simulate GPS location".localized case .settings: - return "Configure StikDebug" + return "Configure StikDebug".localized } } var toolTitle: String { switch self { case .location: - return "Location Simulation" + return "Location Simulation".localized default: return title } diff --git a/StikJIT/Utilities/LanguageManager.swift b/StikJIT/Utilities/LanguageManager.swift new file mode 100644 index 0000000..ba5738d --- /dev/null +++ b/StikJIT/Utilities/LanguageManager.swift @@ -0,0 +1,18 @@ +import SwiftUI + +@Observable +class LanguageManager { + static let shared = LanguageManager() + + var language: String { + didSet { + UserDefaults.standard.set([language], forKey: "AppleLanguages") + UserDefaults.standard.synchronize() + } + } + + init() { + let saved = UserDefaults.standard.stringArray(forKey: "AppleLanguages")?.first ?? "zh-Hans" + self.language = saved + } +} diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index eb5e775..e0a7652 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -220,7 +220,7 @@ struct HomeView: View { ConsoleLogsView() .toolbar { ToolbarItem(placement: .cancellationAction) { - Button("Close") { + Button("Close".localized) { isShowingConsole = false } } @@ -233,7 +233,7 @@ struct HomeView: View { RunJSView(model: jsModel) .toolbar { ToolbarItem(placement: .topBarTrailing) { - Button("Done") { scriptViewShow = false } + Button("Done".localized) { scriptViewShow = false } } } .navigationTitle(selectedScript) diff --git a/StikJIT/Views/InstalledAppsListView.swift b/StikJIT/Views/InstalledAppsListView.swift index 6065014..0e8a8c0 100644 --- a/StikJIT/Views/InstalledAppsListView.swift +++ b/StikJIT/Views/InstalledAppsListView.swift @@ -133,8 +133,8 @@ private enum AppListTab: Int, CaseIterable, Identifiable { var title: String { switch self { - case .debuggable: return "JIT" - case .launch: return "Other" + case .debuggable: return "JIT".localized + case .launch: return "Other".localized } } } @@ -186,7 +186,7 @@ private enum AppListTab: Int, CaseIterable, Identifiable { } if showDoneButton { ToolbarItem(placement: .navigationBarTrailing) { - Button("Done") { dismiss() }.fontWeight(.semibold) + Button("Done".localized) { dismiss() }.fontWeight(.semibold) } } else { ToolbarItem(placement: .navigationBarTrailing) { @@ -580,7 +580,7 @@ struct AppButton: View { .contextMenu { Button(action: toggleFavorite) { Label( - favoriteApps.contains(bundleID) ? "Remove Favorite" : "Add to Favorites", + favoriteApps.contains(bundleID) ? "Remove Favorite".localized : "Add to Favorites".localized, systemImage: favoriteApps.contains(bundleID) ? "star.slash" : "star" ) .disabled(!favoriteApps.contains(bundleID) && favoriteApps.count >= 4) @@ -588,17 +588,17 @@ struct AppButton: View { Button { copyBundleID() } label: { - Label("Copy Bundle ID", systemImage: "doc.on.doc") + Label("Copy Bundle ID".localized, systemImage: "doc.on.doc") } if enableAdvancedOptions { Button { showScriptPicker = true } label: { - Label("Assign Script", systemImage: "chevron.left.slash.chevron.right") + Label("Assign Script".localized, systemImage: "chevron.left.slash.chevron.right") } if assignedScriptName != nil { Button { resetScriptAssignment() } label: { - Label("Reset Script", systemImage: "arrow.uturn.left") + Label("Reset Script".localized, systemImage: "arrow.uturn.left") } } } @@ -607,14 +607,14 @@ struct AppButton: View { Button { toggleFavorite() } label: { - Label(favoriteApps.contains(bundleID) ? "Unfavorite" : "Favorite", systemImage: "star") + Label(favoriteApps.contains(bundleID) ? "Unfavorite".localized : "Favorite".localized, systemImage: "star") } .tint(.yellow) Button { copyBundleID() } label: { - Label("Copy ID", systemImage: "doc.on.doc") + Label("Copy ID".localized, systemImage: "doc.on.doc") } } .sheet(isPresented: $showScriptPicker) { diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 41f31c8..94a5dde 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -18,6 +18,7 @@ struct SettingsView: View { @AppStorage("keepAliveAudio") private var keepAliveAudio = true @AppStorage("keepAliveLocation") private var keepAliveLocation = true @AppStorage("customTargetIP") private var customTargetIP = "" + @Environment(LanguageManager.self) private var langManager @State private var isShowingPairingFilePicker = false @State private var isImportingFile = false @@ -54,15 +55,26 @@ struct SettingsView: View { Section { Link(destination: SettingsLinks.githubStars) { - Label("Star on GitHub", systemImage: "star") + Label("Star on GitHub".localized, systemImage: "star") } } - Section("Pairing File") { + Section("Language".localized) { + Picker("Language".localized, selection: Binding( + get: { langManager.language }, + set: { langManager.language = $0 } + )) { + Text("简体中文").tag("zh-Hans") + Text("English").tag("en") + } + .pickerStyle(.segmented) + } + + Section("Pairing File".localized) { Button { isShowingPairingFilePicker = true } label: { - Label("Import Pairing File", systemImage: "doc.badge.plus") + Label("Import Pairing File".localized, systemImage: "doc.badge.plus") } .disabled(isImportingFile) @@ -70,7 +82,7 @@ struct SettingsView: View { HStack(spacing: 10) { ProgressView() .controlSize(.small) - Text("Importing pairing file…") + Text("Importing pairing file…".localized) .font(.caption) .foregroundStyle(.secondary) } @@ -87,8 +99,8 @@ struct SettingsView: View { Section { Toggle(isOn: $keepAliveAudio) { VStack(alignment: .leading, spacing: 2) { - Text("Silent Audio") - Text("Plays inaudible audio so iOS keeps the app running.") + Text("Silent Audio".localized) + Text("Plays inaudible audio so iOS keeps the app running.".localized) .font(.caption).foregroundStyle(.secondary) } } @@ -99,8 +111,8 @@ struct SettingsView: View { Toggle(isOn: $keepAliveLocation) { VStack(alignment: .leading, spacing: 2) { - Text("Background Location") - Text("Uses low-accuracy location to stay alive when an activity needs it.") + Text("Background Location".localized) + Text("Uses low-accuracy location to stay alive when an activity needs it.".localized) .font(.caption).foregroundStyle(.secondary) } } @@ -109,22 +121,22 @@ struct SettingsView: View { } } header: { - Text("Background Keep-Alive") + Text("Background Keep-Alive".localized) } - Section("Behavior") { + Section("Behavior".localized) { Toggle(isOn: $overrideTXMDetection) { VStack(alignment: .leading, spacing: 2) { - Text("Always Run Scripts") - Text("Treats device as TXM-capable to bypass hardware checks.") + Text("Always Run Scripts".localized) + Text("Treats device as TXM-capable to bypass hardware checks.".localized) .font(.caption).foregroundStyle(.secondary) } } } - Section("Advanced") { + Section("Advanced".localized) { HStack { - Text("Target Device IP") + Text("Target Device IP".localized) Spacer() TextField("10.7.0.1", text: $customTargetIP) .multilineTextAlignment(.trailing) @@ -132,10 +144,10 @@ struct SettingsView: View { .submitLabel(.done) } Button { openAppFolder() } label: { - Label("App Folder", systemImage: "folder") + Label("App Folder".localized, systemImage: "folder") }.foregroundStyle(.primary) Button { showDDIConfirmation = true } label: { - Label("Redownload DDI", systemImage: "arrow.down.circle") + Label("Redownload DDI".localized, systemImage: "arrow.down.circle") }.foregroundStyle(.primary).disabled(isRedownloadingDDI) if isRedownloadingDDI { VStack(alignment: .leading, spacing: 4) { @@ -147,15 +159,15 @@ struct SettingsView: View { } } - Section("Help") { + Section("Help".localized) { Link(destination: SettingsLinks.pairingFileGuide) { - Label("Pairing File Guide", systemImage: "questionmark.circle") + Label("Pairing File Guide".localized, systemImage: "questionmark.circle") } Link(destination: SettingsLinks.localDevVPN) { - Label("Download LocalDevVPN", systemImage: "arrow.down.circle") + Label("Download LocalDevVPN".localized, systemImage: "arrow.down.circle") } Link(destination: SettingsLinks.discord) { - Label("Discord Support", systemImage: "bubble.left.and.bubble.right") + Label("Discord Support".localized, systemImage: "bubble.left.and.bubble.right") } } @@ -166,7 +178,7 @@ struct SettingsView: View { .listRowBackground(Color.clear) } } - .navigationTitle("Settings") + .navigationTitle("Settings".localized) } .fileImporter( isPresented: $isShowingPairingFilePicker, @@ -184,27 +196,27 @@ struct SettingsView: View { do { try PairingFileStore.importFromPicker(url, fileManager: fileManager) isImportingFile = false - pairingImportMessage = ("Imported successfully", false) + pairingImportMessage = ("Imported successfully".localized, false) startTunnelInBackground() schedulePairingStatusDismiss() } catch { isImportingFile = false - pairingImportMessage = ("Import failed: \(error.localizedDescription)", true) + pairingImportMessage = (String(format: "Import failed: %@".localized, error.localizedDescription), true) schedulePairingStatusDismiss() } case .failure(let error): isImportingFile = false - pairingImportMessage = ("Import failed: \(error.localizedDescription)", true) + pairingImportMessage = (String(format: "Import failed: %@".localized, error.localizedDescription), true) schedulePairingStatusDismiss() } } - .confirmationDialog("Redownload DDI Files?", isPresented: $showDDIConfirmation, titleVisibility: .visible) { - Button("Redownload", role: .destructive) { + .confirmationDialog("Redownload DDI Files?".localized, isPresented: $showDDIConfirmation, titleVisibility: .visible) { + Button("Redownload".localized, role: .destructive) { redownloadDDIPressed() } - Button("Cancel", role: .cancel) { } + Button("Cancel".localized, role: .cancel) { } } message: { - Text("Existing DDI files will be removed before downloading fresh copies.") + Text("Existing DDI files will be removed before downloading fresh copies.".localized) } } @@ -235,7 +247,7 @@ struct SettingsView: View { await MainActor.run { isRedownloadingDDI = true ddiDownloadProgress = 0 - ddiStatusMessage = "Preparing download…" + ddiStatusMessage = "Preparing download…".localized ddiResultMessage = nil } do { @@ -247,12 +259,12 @@ struct SettingsView: View { } await MainActor.run { isRedownloadingDDI = false - ddiResultMessage = ("DDI files refreshed successfully.", false) + ddiResultMessage = ("DDI files refreshed successfully.".localized, false) } } catch { await MainActor.run { isRedownloadingDDI = false - ddiResultMessage = ("Failed to redownload DDI files: \(error.localizedDescription)", true) + ddiResultMessage = (String(format: "Failed to redownload DDI files: %@".localized, error.localizedDescription), true) } } }