fix(i18n): 修复首选语言回退并准备 v1.0.8

This commit is contained in:
xweiba
2026-09-24 23:19:21 +08:00
parent 228b67b210
commit 5b9fedfe9e
34 changed files with 1640 additions and 173 deletions
+14 -14
View File
@@ -95,10 +95,10 @@ struct BugReportView: View {
do {
let response = try await thirdPartyProxy.query()
let active = response.success && response.latitude != nil && response.longitude != nil
let savedCoordinate = active ? String(localized: "是") : String(localized: "否")
testLog = String(localized: "第三方代理测试模式:模块连接成功;已保存坐标=\(savedCoordinate)")
let savedCoordinate = active ? AppLocalization.string("是") : AppLocalization.string("否")
testLog = AppLocalization.string("第三方代理测试模式:模块连接成功;已保存坐标=\(savedCoordinate)")
} catch {
testLog = String(localized: "第三方代理测试模式:模块连接失败;\(error.localizedDescription)")
testLog = AppLocalization.string("第三方代理测试模式:模块连接失败;\(error.localizedDescription)")
}
} else {
_ = await setup.runVerificationTest()
@@ -132,21 +132,21 @@ struct BugReportView: View {
private func bugReport(appVersion: String, systemVersion: String, testLog: String) -> String {
let client = runtimeMode.mode == .thirdParty
? thirdPartyClient.selectedClient.name
: String(localized: "不适用")
let reproducible = isReproducible ? String(localized: "是") : String(localized: "否")
let diagnostics = testLog.isEmpty ? String(localized: "(无诊断数据)") : testLog
: AppLocalization.string("不适用")
let reproducible = isReproducible ? AppLocalization.string("是") : AppLocalization.string("否")
let diagnostics = testLog.isEmpty ? AppLocalization.string("(无诊断数据)") : testLog
return """
### \(String(localized: "环境信息"))
\(String(localized: "App 版本")): \(appVersion)
\(String(localized: "系统版本")): iOS \(systemVersion)
\(String(localized: "运行模式")): \(runtimeMode.mode.displayName)
\(String(localized: "第三方客户端")): \(client)
\(String(localized: "可复现环境")): \(reproducible)
### \(AppLocalization.string("环境信息"))
\(AppLocalization.string("App 版本")): \(appVersion)
\(AppLocalization.string("系统版本")): iOS \(systemVersion)
\(AppLocalization.string("运行模式")): \(runtimeMode.mode.displayName)
\(AppLocalization.string("第三方客户端")): \(client)
\(AppLocalization.string("可复现环境")): \(reproducible)
### \(String(localized: "问题描述"))
### \(AppLocalization.string("问题描述"))
\(description.trimmingCharacters(in: .whitespacesAndNewlines))
### \(String(localized: "诊断日志"))
### \(AppLocalization.string("诊断日志"))
```
\(diagnostics)
```
+2 -2
View File
@@ -177,7 +177,7 @@ struct ContentView: View {
switch prompt.requirement {
case .required:
let details = prompt.releaseNotes
?? String(localized: "更新说明暂时无法加载,请前往最新 Release 页面查看。")
?? AppLocalization.string("更新说明暂时无法加载,请前往最新 Release 页面查看。")
return Alert(
title: Text("需要更新"),
message: Text(
@@ -189,7 +189,7 @@ struct ContentView: View {
)
case .recommended:
let details = prompt.releaseNotes
?? String(localized: "更新说明暂时无法加载,请前往最新 Release 页面查看。")
?? AppLocalization.string("更新说明暂时无法加载,请前往最新 Release 页面查看。")
return Alert(
title: Text("发现新版本"),
message: Text(
+11 -11
View File
@@ -109,9 +109,9 @@ struct RuntimeLogsView: View {
let result = await setup.runVerificationTest()
testSucceeded = result.isSuccess
testResult = result.isSuccess
? String(localized: "环境检测通过")
: String(localized: "环境检测失败: \(result.localizedTitle)")
if !result.isSuccess { testResult += String(localized: ",查看下方日志") }
? AppLocalization.string("环境检测通过")
: AppLocalization.string("环境检测失败: \(result.localizedTitle)")
if !result.isSuccess { testResult += AppLocalization.string(",查看下方日志") }
testMessage = setup.testLog
}
isTesting = false; refresh()
@@ -248,26 +248,26 @@ struct RuntimeLogsView: View {
let response = try await thirdPartyProxy.query()
let active = response.success && response.latitude != nil && response.longitude != nil
testSucceeded = true
testResult = active ? String(localized: "第三方模块连接通过,已有坐标") : String(localized: "第三方模块连接通过,暂无坐标")
testResult = active ? AppLocalization.string("第三方模块连接通过,已有坐标") : AppLocalization.string("第三方模块连接通过,暂无坐标")
testMessage = thirdPartyTestLog(active: active)
} catch {
testSucceeded = false
testResult = String(localized: "第三方模块连接失败")
testResult = AppLocalization.string("第三方模块连接失败")
testMessage = thirdPartyTestLog(error: error)
}
}
private func thirdPartyTestLog(active: Bool? = nil, error: Error? = nil) -> String {
var lines = [
String(localized: "======== 第三方代理连接检测 ========"),
String(localized: "模式: 测试模式"),
String(localized: "请求: wloc-settings/save?action=query")
AppLocalization.string("======== 第三方代理连接检测 ========"),
AppLocalization.string("模式: 测试模式"),
AppLocalization.string("请求: wloc-settings/save?action=query")
]
if let active {
lines.append(String(localized: "拦截响应: 有效 JSON"))
lines.append(String(localized: "已保存坐标: \(active ? String(localized: "是") : String(localized: "否"))"))
lines.append(AppLocalization.string("拦截响应: 有效 JSON"))
lines.append(AppLocalization.string("已保存坐标: \(active ? AppLocalization.string("是") : AppLocalization.string("否"))"))
} else if let error {
lines.append(String(localized: "结果: \(error.localizedDescription)"))
lines.append(AppLocalization.string("结果: \(error.localizedDescription)"))
}
return lines.joined(separator: "\n")
}
+34 -34
View File
@@ -221,13 +221,13 @@ struct FirstSetupView: View {
}
guard !setup.message.isEmpty else { return nil }
return [
String(localized: "======== 第三方代理运行检测 ========"),
String(localized: "当前客户端:\(thirdPartyClient.selectedClient.name)"),
String(localized: "触发来源:地图或设置中的第三方代理操作"),
String(localized: "请求动作:WLOC 配置接口"),
String(localized: "检测结果:失败"),
String(localized: "错误详情:\(setup.message)"),
String(localized: "处理建议:确认模块已启用,并检查 MITM、证书和代理/VPN 连接。")
AppLocalization.string("======== 第三方代理运行检测 ========"),
AppLocalization.string("当前客户端:\(thirdPartyClient.selectedClient.name)"),
AppLocalization.string("触发来源:地图或设置中的第三方代理操作"),
AppLocalization.string("请求动作:WLOC 配置接口"),
AppLocalization.string("检测结果:失败"),
AppLocalization.string("错误详情:\(setup.message)"),
AppLocalization.string("处理建议:确认模块已启用,并检查 MITM、证书和代理/VPN 连接。")
].joined(separator: "\n")
}
@@ -354,7 +354,7 @@ struct FirstSetupView: View {
if let url = await setup.proxy.prepareCertificateDownloadURL() {
certificateDownloadDestination = CertificateDownloadDestination(url: url)
} else {
setupActionError = setup.proxy.error ?? String(localized: "无法准备证书下载页面,请查看诊断日志")
setupActionError = setup.proxy.error ?? AppLocalization.string("无法准备证书下载页面,请查看诊断日志")
}
}
},
@@ -468,7 +468,7 @@ struct FirstSetupView: View {
Text("返回格式")
.font(.subheadline.bold())
Text(String(localized: """
Text(AppLocalization.string("""
成功:{"success":true,"longitude":113.0,"latitude":22.0,"accuracy":25}
失败:{"success":false,"error":"错误说明"}
"""))
@@ -565,7 +565,7 @@ struct FirstSetupView: View {
if let thirdPartyFailureLog {
testResultView(
success: false,
title: String(localized: "接口连接失败"),
title: AppLocalization.string("接口连接失败"),
log: thirdPartyFailureLog
)
.id("thirdPartyFailureLog")
@@ -698,7 +698,7 @@ struct FirstSetupView: View {
UIApplication.shared.open(url, options: [:]) { opened in
guard !opened else { return }
Task { @MainActor in
manualHint = String(localized: "无法打开 \(client.name),请确认客户端已安装后手动打开。")
manualHint = AppLocalization.string("无法打开 \(client.name),请确认客户端已安装后手动打开。")
}
}
}
@@ -760,7 +760,7 @@ struct FirstSetupView: View {
let success = result.isSuccess
testResultView(
success: success,
title: success ? String(localized: "环境检测通过") : failureSummary(result),
title: success ? AppLocalization.string("环境检测通过") : failureSummary(result),
log: setup.testLog
)
}
@@ -889,17 +889,17 @@ struct FirstSetupView: View {
]
)
thirdPartyTestFailure = ThirdPartyConnectionTestFailure(message: [
String(localized: "======== 第三方代理连接检测 ========"),
String(localized: "当前客户端:\(client.name)"),
String(localized: "配置接口:/wloc-settings/save"),
String(localized: "请求动作:WLOC query"),
String(localized: "检查范围:模块拦截、MITM、证书、代理/VPN 连接"),
String(localized: "连接状态:\(connectionState)"),
String(localized: "检测结果:失败"),
String(localized: "耗时:\(elapsedMilliseconds) ms"),
String(localized: "错误类型:\(errorType)"),
String(localized: "错误详情:\(error.localizedDescription)"),
String(localized: "处理建议:\(ThirdPartyProxyError.recoverySuggestion(for: error))。")
AppLocalization.string("======== 第三方代理连接检测 ========"),
AppLocalization.string("当前客户端:\(client.name)"),
AppLocalization.string("配置接口:/wloc-settings/save"),
AppLocalization.string("请求动作:WLOC query"),
AppLocalization.string("检查范围:模块拦截、MITM、证书、代理/VPN 连接"),
AppLocalization.string("连接状态:\(connectionState)"),
AppLocalization.string("检测结果:失败"),
AppLocalization.string("耗时:\(elapsedMilliseconds) ms"),
AppLocalization.string("错误类型:\(errorType)"),
AppLocalization.string("错误详情:\(error.localizedDescription)"),
AppLocalization.string("处理建议:\(ThirdPartyProxyError.recoverySuggestion(for: error))。")
].joined(separator: "\n"))
showsThirdPartyFailureLog = true
}
@@ -909,11 +909,11 @@ struct FirstSetupView: View {
private var thirdPartyConnectionStateDescription: String {
switch thirdPartyProxy.connectionState {
case .unknown:
return String(localized: "未检测")
return AppLocalization.string("未检测")
case .connected(let active):
return active ? String(localized: "已连接,有保存坐标") : String(localized: "已连接,无保存坐标")
return active ? AppLocalization.string("已连接,有保存坐标") : AppLocalization.string("已连接,无保存坐标")
case .failed(let message):
return String(localized: "连接失败(\(message))")
return AppLocalization.string("连接失败(\(message))")
}
}
@@ -966,14 +966,14 @@ struct FirstSetupView: View {
private func failureSummary(_ result: VerificationResult) -> String {
switch result {
case .certNotTrusted: return String(localized: "证书尚未安装或信任")
case .wifiProxyNotConfigured: return String(localized: "Wi-Fi 代理未正确设置")
case .proxyNotRunning: return String(localized: "本地代理未能启动")
case .verificationInProgress: return String(localized: "检测仍在进行")
case .verificationSuperseded: return String(localized: "检测结果已过期")
case .coordinateWriteFailed: return String(localized: "坐标写入失败")
case .patchFailed: return String(localized: "定位改写检测失败")
case .success: return String(localized: "环境检测通过")
case .certNotTrusted: return AppLocalization.string("证书尚未安装或信任")
case .wifiProxyNotConfigured: return AppLocalization.string("Wi-Fi 代理未正确设置")
case .proxyNotRunning: return AppLocalization.string("本地代理未能启动")
case .verificationInProgress: return AppLocalization.string("检测仍在进行")
case .verificationSuperseded: return AppLocalization.string("检测结果已过期")
case .coordinateWriteFailed: return AppLocalization.string("坐标写入失败")
case .patchFailed: return AppLocalization.string("定位改写检测失败")
case .success: return AppLocalization.string("环境检测通过")
}
}
+12 -12
View File
@@ -301,7 +301,7 @@ struct MapHomeView: View {
}
} message: {
Text(
"你正在使用 \(communityContributionClient?.name ?? String(localized: "第三方客户端"))。点击“去提交”会先复制投稿模板,并在 App 内打开社区页面。采纳后将收录到 README,可选择是否匿名署名。"
"你正在使用 \(communityContributionClient?.name ?? AppLocalization.string("第三方客户端"))。点击“去提交”会先复制投稿模板,并在 App 内打开社区页面。采纳后将收录到 README,可选择是否匿名署名。"
)
}
.alert("已复制投稿模板", isPresented: $showCommunityTemplateCopied) {
@@ -483,7 +483,7 @@ struct MapHomeView: View {
// 当前选点
HStack {
VStack(alignment: .leading, spacing: 3) {
Text(mapState.displayName ?? String(localized: "当前选点")).font(.subheadline.weight(.semibold)).lineLimit(1)
Text(mapState.displayName ?? AppLocalization.string("当前选点")).font(.subheadline.weight(.semibold)).lineLimit(1)
coordinateRow(label: "GCJ-02(国内)", system: .gcj02)
coordinateRow(label: "WGS-84(国际)", system: .wgs84)
}
@@ -585,15 +585,15 @@ struct MapHomeView: View {
private var buttonTitle: String {
if runtimeMode.mode == .thirdParty {
switch spoofState {
case .idle: return String(localized: "同步到第三方代理")
case .verifying: return String(localized: "检测并同步中…")
case .active: return String(localized: "停止第三方虚拟定位")
case .idle: return AppLocalization.string("同步到第三方代理")
case .verifying: return AppLocalization.string("检测并同步中…")
case .active: return AppLocalization.string("停止第三方虚拟定位")
}
}
switch spoofState {
case .idle: return String(localized: "开始虚拟定位")
case .verifying: return String(localized: "验证环境中…")
case .active: return String(localized: "停止虚拟定位")
case .idle: return AppLocalization.string("开始虚拟定位")
case .verifying: return AppLocalization.string("验证环境中…")
case .active: return AppLocalization.string("停止虚拟定位")
}
}
@@ -1092,7 +1092,7 @@ struct MapHomeView: View {
"请求动作": "WLOC query",
"错误": response.error ?? "未知错误"
])
setup.requestThirdPartySetup(message: response.error ?? String(localized: "第三方代理查询失败"))
setup.requestThirdPartySetup(message: response.error ?? AppLocalization.string("第三方代理查询失败"))
}
} catch {
spoofState = .idle
@@ -1145,7 +1145,7 @@ struct MapHomeView: View {
"Wi-Fi接口": String(net.isWiFiEnabled)
])
activeTip = nil
setup.requestSetup(message: String(localized: "当前未连接可用的 Wi-Fi,请连接 Wi-Fi 后配置 127.0.0.1:8888 手动代理。"))
setup.requestSetup(message: AppLocalization.string("当前未连接可用的 Wi-Fi,请连接 Wi-Fi 后配置 127.0.0.1:8888 手动代理。"))
return
}
@@ -1520,7 +1520,7 @@ struct MapHomeView: View {
}
searchResults = (response?.mapItems ?? []).prefix(6).map { item in
let r = SearchLocationResult(
name: item.name ?? String(localized: "未命名"),
name: item.name ?? AppLocalization.string("未命名"),
subtitle: [item.placemark.locality, item.placemark.subLocality, item.placemark.thoroughfare]
.compactMap { $0 }
.filter { !$0.isEmpty }
@@ -1532,7 +1532,7 @@ struct MapHomeView: View {
])
return r
}
if searchResults.isEmpty { searchError = String(localized: "没有找到相关地点") }
if searchResults.isEmpty { searchError = AppLocalization.string("没有找到相关地点") }
}
}
}
+1
View File
@@ -9,6 +9,7 @@ struct PaopaoLocationSpooferApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.locale, AppLocalization.locale)
}
}
}
+3 -3
View File
@@ -154,13 +154,13 @@ final class ProxyManager: ObservableObject {
do {
if !isRunning { try await start() }
guard let url = URL(string: "http://127.0.0.1:8888/cert") else {
error = String(localized: "证书下载地址无效")
error = AppLocalization.string("证书下载地址无效")
return nil
}
error = nil
return url
} catch {
self.error = String(localized: "启动代理失败: \(error.localizedDescription)")
self.error = AppLocalization.string("启动代理失败: \(error.localizedDescription)")
RuntimeLogger.error("APP", "Certificate", "准备证书下载失败", error: error)
return nil
}
@@ -182,5 +182,5 @@ struct ProxyCoordinateSnapshot: Equatable {
enum ProxyError: LocalizedError {
case startFailed
var errorDescription: String? { String(localized: "Go proxy 启动失败") }
var errorDescription: String? { AppLocalization.string("Go proxy 启动失败") }
}
+21 -21
View File
@@ -30,7 +30,7 @@ struct SettingsView: View {
@Environment(\.dismiss) private var dismiss
@State private var activeTip: TipKind?
@State private var proxyOperationError = ""
@State private var proxyOperationAlertTitle = String(localized: "代理操作失败")
@State private var proxyOperationAlertTitle = AppLocalization.string("代理操作失败")
@State private var modeOperationRunning = false
@State private var copiedClient: ThirdPartyProxyClient?
@State private var copiedMITMHostname: String?
@@ -319,12 +319,12 @@ struct SettingsView: View {
)
case .available(let prompt):
let details = prompt.releaseNotes
?? String(localized: "更新说明暂时无法加载,请前往最新 Release 页面查看。")
?? AppLocalization.string("更新说明暂时无法加载,请前往最新 Release 页面查看。")
let message: String
if prompt.requirement == .required {
message = String(localized: "当前版本 \(prompt.currentVersion) 已停止支持,请更新到 \(prompt.latestVersion) 后继续使用。\n\n\(details)")
message = AppLocalization.string("当前版本 \(prompt.currentVersion) 已停止支持,请更新到 \(prompt.latestVersion) 后继续使用。\n\n\(details)")
} else {
message = String(localized: "当前版本 \(prompt.currentVersion),最新版本 \(prompt.latestVersion)。\n\n\(details)")
message = AppLocalization.string("当前版本 \(prompt.currentVersion),最新版本 \(prompt.latestVersion)。\n\n\(details)")
}
return Alert(
title: prompt.requirement == .required ? Text("需要更新") : Text("发现新版本"),
@@ -351,7 +351,7 @@ struct SettingsView: View {
try await proxy.start()
} catch {
proxy.error = error.localizedDescription
proxyOperationAlertTitle = String(localized: "代理操作失败")
proxyOperationAlertTitle = AppLocalization.string("代理操作失败")
proxyOperationError = error.localizedDescription
}
} else {
@@ -482,27 +482,27 @@ struct SettingsView: View {
private var thirdPartyStatusText: String {
switch thirdPartyProxy.connectionState {
case .unknown: return String(localized: "未检测")
case .connected(let active): return active ? String(localized: "已连接,有坐标") : String(localized: "已连接,无坐标")
case .failed: return String(localized: "连接失败")
case .unknown: return AppLocalization.string("未检测")
case .connected(let active): return active ? AppLocalization.string("已连接,有坐标") : AppLocalization.string("已连接,无坐标")
case .failed: return AppLocalization.string("连接失败")
}
}
private var virtualLocationStatusText: String {
if runtimeMode.mode == .localWiFi {
return actions.virtualLocationEnabled ? String(localized: "已开启") : String(localized: "已关闭")
return actions.virtualLocationEnabled ? AppLocalization.string("已开启") : AppLocalization.string("已关闭")
}
if case .connected(let active) = thirdPartyProxy.connectionState {
return active ? String(localized: "第三方已保存") : String(localized: "未保存")
return active ? AppLocalization.string("第三方已保存") : AppLocalization.string("未保存")
}
return String(localized: "未知")
return AppLocalization.string("未知")
}
private var workflowDescription: String {
if runtimeMode.mode == .thirdParty {
return String(localized: "App 只负责地图选点、收藏和发送 WGS-84 坐标。第三方代理客户端通过模块拦截 Apple WLOC 请求并持久化当前坐标;本模式不启动本机代理,不使用 App 的 CA,也不需要配置 127.0.0.1:8888。")
return AppLocalization.string("App 只负责地图选点、收藏和发送 WGS-84 坐标。第三方代理客户端通过模块拦截 Apple WLOC 请求并持久化当前坐标;本模式不启动本机代理,不使用 App 的 CA,也不需要配置 127.0.0.1:8888。")
}
return String(localized: """
return AppLocalization.string("""
App 在设备本地运行一个代理服务器(127.0.0.1:8888)。
通过 WiFi 手动代理配置,让系统发往 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的定位请求经过这个本地代理。代理使用已安装的 CA 证书对 HTTPS 流量做中间人解密,把 Apple 返回的定位坐标改写为你设置的虚拟坐标,再加密返回给系统,从而实现虚拟定位。
@@ -523,8 +523,8 @@ struct SettingsView: View {
if runtimeMode.isInitialized(.thirdParty) {
do {
_ = try await thirdPartyProxy.query()
proxyOperationAlertTitle = String(localized: "模式已切换")
proxyOperationError = String(localized: "第三方代理模式检测通过。请关闭 Wi-Fi 中的 127.0.0.1:8888 手动代理,避免双重拦截。")
proxyOperationAlertTitle = AppLocalization.string("模式已切换")
proxyOperationError = AppLocalization.string("第三方代理模式检测通过。请关闭 Wi-Fi 中的 127.0.0.1:8888 手动代理,避免双重拦截。")
} catch {
openThirdPartySetup(for: error)
}
@@ -546,8 +546,8 @@ struct SettingsView: View {
let result = await setup.runVerificationTest()
setup.applyVerificationResult(result)
if result.isSuccess {
proxyOperationAlertTitle = String(localized: "模式已切换")
proxyOperationError = String(localized: "APP 模式环境检测通过。请停用第三方 WLOC 模块或代理连接,避免双重拦截。")
proxyOperationAlertTitle = AppLocalization.string("模式已切换")
proxyOperationError = AppLocalization.string("APP 模式环境检测通过。请停用第三方 WLOC 模块或代理连接,避免双重拦截。")
} else {
dismiss()
}
@@ -608,14 +608,14 @@ struct SettingsView: View {
try setup.certificateStore.reset()
runtimeMode.resetInitialization(.localWiFi)
guard await setup.prepareLocalServices() else {
proxyOperationAlertTitle = String(localized: "证书重置失败")
proxyOperationAlertTitle = AppLocalization.string("证书重置失败")
proxyOperationError = setup.message
return
}
setup.requestCertificateSetup()
dismiss()
} catch {
proxyOperationAlertTitle = String(localized: "证书重置失败")
proxyOperationAlertTitle = AppLocalization.string("证书重置失败")
proxyOperationError = error.localizedDescription
}
}
@@ -626,8 +626,8 @@ struct SettingsView: View {
UIApplication.shared.open(url, options: [:]) { opened in
guard !opened else { return }
Task { @MainActor in
proxyOperationAlertTitle = String(localized: "无法打开客户端")
proxyOperationError = String(localized: "无法打开 \(client.name),请确认客户端已安装后手动打开。")
proxyOperationAlertTitle = AppLocalization.string("无法打开客户端")
proxyOperationError = AppLocalization.string("无法打开 \(client.name),请确认客户端已安装后手动打开。")
}
}
}
+23 -23
View File
@@ -36,7 +36,7 @@ final class SetupCoordinator: ObservableObject {
message = ""
return true
} catch {
message = String(localized: "本地代理初始化失败:\(error.localizedDescription)")
message = AppLocalization.string("本地代理初始化失败:\(error.localizedDescription)")
RuntimeLogger.error("APP", "Startup", "本地服务初始化失败", error: error)
return false
}
@@ -48,19 +48,19 @@ final class SetupCoordinator: ObservableObject {
case .success:
trustState = .trusted
needsSetup = false
message = String(localized: "✓ 定位环境正常")
message = AppLocalization.string("✓ 定位环境正常")
case .certNotTrusted:
trustState = .unavailable
setupStep = .cert
needsSetup = true
message = String(localized: "CA 证书未安装或未信任")
message = AppLocalization.string("CA 证书未安装或未信任")
case .verificationInProgress, .verificationSuperseded:
break
default:
trustState = .unavailable
setupStep = .proxy
needsSetup = true
message = String(localized: "Wi-Fi 代理未正确设置,请检查 127.0.0.1:8888")
message = AppLocalization.string("Wi-Fi 代理未正确设置,请检查 127.0.0.1:8888")
}
}
@@ -108,36 +108,36 @@ final class SetupCoordinator: ObservableObject {
testLog = ""
let log = { (msg: String) in self.testLog += msg + "\n" }
log(String(localized: "======== 代理验证测试 ========"))
log(String(localized: "App 版本: \(appVersion)"))
log(String(localized: "系统版本: iOS \(UIDevice.current.systemVersion)"))
log(AppLocalization.string("======== 代理验证测试 ========"))
log(AppLocalization.string("App 版本: \(appVersion)"))
log(AppLocalization.string("系统版本: iOS \(UIDevice.current.systemVersion)"))
log("")
// Step A: Proxy running
log(String(localized: "[步骤 A] 检查代理是否运行…"))
log(String(localized: " 端口: 127.0.0.1:8888"))
log(AppLocalization.string("[步骤 A] 检查代理是否运行…"))
log(AppLocalization.string(" 端口: 127.0.0.1:8888"))
let stepAStart = Date()
if !proxy.isRunning {
log(String(localized: " ⚠ 代理未运行,尝试启动…"))
log(AppLocalization.string(" ⚠ 代理未运行,尝试启动…"))
do { try await proxy.start() } catch {
log(String(localized: " ✗ 启动失败: \(error.localizedDescription)"))
log(AppLocalization.string(" ✗ 启动失败: \(error.localizedDescription)"))
return .proxyNotRunning
}
log(String(localized: " ✓ 代理启动成功"))
log(AppLocalization.string(" ✓ 代理启动成功"))
} else {
log(String(localized: " ✓ 代理已在运行中"))
log(AppLocalization.string(" ✓ 代理已在运行中"))
}
collectProxyLogs(since: stepAStart, to: log)
// Step B: Combined CA + WiFi proxy check (single request)
log("")
log(String(localized: "[步骤 B] 检测证书与 WiFi 代理…"))
log(String(localized: " 方式: 请求 baidu.com/paopao-verify-<token>"))
log(String(localized: " 结果判定: TLS 错误=证书问题 / 响应不匹配=代理未配置 / 匹配=通过"))
log(AppLocalization.string("[步骤 B] 检测证书与 WiFi 代理…"))
log(AppLocalization.string(" 方式: 请求 baidu.com/paopao-verify-<token>"))
log(AppLocalization.string(" 结果判定: TLS 错误=证书问题 / 响应不匹配=代理未配置 / 匹配=通过"))
let stepBStart = Date()
let verifyToken = CoreBridge.refreshVerifyToken()
guard !verifyToken.isEmpty else {
log(String(localized: " ✗ 无法生成验证 token"))
log(AppLocalization.string(" ✗ 无法生成验证 token"))
return .certNotTrusted
}
do {
@@ -150,17 +150,17 @@ final class SetupCoordinator: ObservableObject {
let statusCode = (resp as? HTTPURLResponse)?.statusCode ?? 0
let body = String(data: data, encoding: .utf8) ?? ""
if body == verifyToken {
log(String(localized: " ✓ 证书已信任,WiFi 代理已配置"))
log(AppLocalization.string(" ✓ 证书已信任,WiFi 代理已配置"))
} else {
log(String(localized: " ✗ 响应不匹配: HTTP \(statusCode), \(data.count) bytes,WiFi 代理未配置"))
log(AppLocalization.string(" ✗ 响应不匹配: HTTP \(statusCode), \(data.count) bytes,WiFi 代理未配置"))
return .wifiProxyNotConfigured
}
} catch {
let ns = error as NSError
let msg = error.localizedDescription
log(String(localized: " ✗ 请求失败 [\(ns.domain) code=\(ns.code)]: \(msg)"))
log(AppLocalization.string(" ✗ 请求失败 [\(ns.domain) code=\(ns.code)]: \(msg)"))
if isCertificateTrustError(nsError: ns, message: msg) {
log(String(localized: " TLS/证书校验失败,CA 证书未信任"))
log(AppLocalization.string(" TLS/证书校验失败,CA 证书未信任"))
return .certNotTrusted
}
return .wifiProxyNotConfigured
@@ -168,7 +168,7 @@ final class SetupCoordinator: ObservableObject {
collectProxyLogs(since: stepBStart, to: log)
log("")
log(String(localized: "======== 环境检测通过 ✓ ========"))
log(AppLocalization.string("======== 环境检测通过 ✓ ========"))
return .success
}
@@ -202,7 +202,7 @@ final class SetupCoordinator: ObservableObject {
$0.source == "CORE" && $0.category == "Proxy" && $0.timestamp >= date
}
guard !proxyEntries.isEmpty else { return }
log(String(localized: " --- 代理日志 ---"))
log(AppLocalization.string(" --- 代理日志 ---"))
for e in proxyEntries {
log(" " + e.localizedMessage)
}
+4 -4
View File
@@ -25,13 +25,13 @@ enum SystemSettingsDestination {
var manualPath: String {
switch self {
case .appPermissions:
return String(localized: "请手动打开「设置」,找到本 App 后检查定位权限。")
return AppLocalization.string("请手动打开「设置」,找到本 App 后检查定位权限。")
case .general:
return String(localized: "请手动打开「设置 → 通用」。")
return AppLocalization.string("请手动打开「设置 → 通用」。")
case .wifi:
return String(localized: "请手动打开「设置 → 无线局域网」,进入当前 Wi-Fi 的详情页。")
return AppLocalization.string("请手动打开「设置 → 无线局域网」,进入当前 Wi-Fi 的详情页。")
case .locationServices:
return String(localized: "请手动打开「设置 → 隐私与安全性 → 定位服务」。")
return AppLocalization.string("请手动打开「设置 → 隐私与安全性 → 定位服务」。")
}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ responses in a controlled test environment.
[![iOS 15+](https://img.shields.io/badge/iOS-15%2B-111111?logo=apple)](project.yml)
[![Swift 5.9](https://img.shields.io/badge/Swift-5.9-F05138)](project.yml)
[![Go 1.23+](https://img.shields.io/badge/Go-1.23%2B-00ADD8?logo=go)](Core/go.mod)
[![Version](https://img.shields.io/badge/version-v1.0.7-2563EB)](docs/CHANGELOG.md)
[![Version](https://img.shields.io/badge/version-v1.0.8-2563EB)](docs/CHANGELOG.md)
[Features](#feature-overview) ·
[How It Works](#how-it-works) ·
+1 -1
View File
@@ -14,7 +14,7 @@
[![iOS 15+](https://img.shields.io/badge/iOS-15%2B-111111?logo=apple)](project.yml)
[![Swift 5.9](https://img.shields.io/badge/Swift-5.9-F05138)](project.yml)
[![Go 1.23+](https://img.shields.io/badge/Go-1.23%2B-00ADD8?logo=go)](Core/go.mod)
[![Version](https://img.shields.io/badge/version-v1.0.7-2563EB)](docs/CHANGELOG.md)
[![Version](https://img.shields.io/badge/version-v1.0.8-2563EB)](docs/CHANGELOG.md)
[功能概览](#功能概览) ·
[工作原理](#工作原理) ·
+5 -3
View File
@@ -1,8 +1,8 @@
/* ============================================================
English localization overlay.
English localization and development-language fallback.
Keys are the original Simplified-Chinese source strings
(development language = zh-Hans). Any key not found here
falls back to the Chinese key text automatically.
All localized keys are mirrored in zh-Hans and zh-Hant tables.
Untranslated keys still display their Chinese source text.
============================================================ */
/* MARK: - Shared models (ProxyRuntimeMode / VerificationResult) */
@@ -60,6 +60,8 @@
"验证状态" = "Verification Status";
"已复制模块订阅地址" = "Subscription URL copied";
"复制模块订阅地址" = "Copy module subscription URL";
"复制 %@" = "Copy %@";
"已复制 %@" = "Copied %@";
"已复制解密域名" = "Decryption hostnames copied";
"复制解密域名" = "Copy decryption hostnames";
"打开 %@" = "Open %@";
+668
View File
@@ -0,0 +1,668 @@
/* Generated from en.lproj/Localizable.strings by Scripts/generate-chinese-localizations.swift. */
"APP模式" = "APP模式";
"第三方代理模式" = "第三方代理模式";
"成功" = "成功";
"已有验证正在进行" = "已有验证正在进行";
"验证已被新位置取代" = "验证已被新位置取代";
"证书未信任" = "证书未信任";
"WiFi代理未配置" = "WiFi代理未配置";
"坐标写入失败" = "坐标写入失败";
"改写验证失败" = "改写验证失败";
"运行模式" = "运行模式";
"模式" = "模式";
"状态" = "状态";
"本机代理" = "本机代理";
"第三方模块" = "第三方模块";
"检测连接" = "检测连接";
"虚拟定位" = "虚拟定位";
"定位模拟" = "定位模拟";
"运动状态模拟" = "运动状态模拟";
"实验性功能,默认关闭。开启后会同时模拟定位响应中的运动状态。" = "实验性功能,默认关闭。开启后会同时模拟定位响应中的运动状态。";
"随机扰动" = "随机扰动";
"开启后,下次同步坐标时会给目标点添加随机偏移(默认半径 50 米),避免位置固定在同一点。" = "开启后,下次同步坐标时会给目标点添加随机偏移(默认半径 50 米),避免位置固定在同一点。";
"说明" = "说明";
"工作原理" = "工作原理";
"应用" = "应用";
"进入引导页" = "进入引导页";
"正在检查…" = "正在检查…";
"检查更新" = "检查更新";
"版本" = "版本";
"证书" = "证书";
"重置证书" = "重置证书";
"仅删除 App 钥匙串中的设备 CA。iOS 中已经安装的旧证书需要在系统设置里手动移除。" = "仅删除 App 钥匙串中的设备 CA。iOS 中已经安装的旧证书需要在系统设置里手动移除。";
"支持" = "支持";
"使用帮助" = "使用帮助";
"功能建议" = "功能建议";
"分享第三方配置" = "分享第三方配置";
"关于" = "关于";
"如果觉得好用,欢迎去 GitHub 给项目点个 Star" = "如果觉得好用,欢迎去 GitHub 给项目点个 Star";
"致谢" = "致谢";
"核心定位改写逻辑移植自 Yu9191/wloc" = "核心定位改写逻辑移植自 Yu9191/wloc";
"设置" = "设置";
"完成" = "完成";
"代理操作失败" = "代理操作失败";
"重置证书?" = "重置证书?";
"重置并生成新证书" = "重置并生成新证书";
"当前虚拟定位和本地代理将停止。App 会删除钥匙串中的设备 CA、立即生成新证书,并打开安装与信任引导。你还需要前往 iOS「设置 → 通用 → VPN 与设备管理」手动删除旧证书,然后重新下载安装并完全信任新证书。" = "当前虚拟定位和本地代理将停止。App 会删除钥匙串中的设备 CA、立即生成新证书,并打开安装与信任引导。你还需要前往 iOS「设置 → 通用 → VPN 与设备管理」手动删除旧证书,然后重新下载安装并完全信任新证书。";
"第三方代理配置" = "第三方代理配置";
"客户端" = "客户端";
"使用国内镜像下载模块" = "使用国内镜像下载模块";
"仅影响之后复制和重新导入的模块地址;已安装模块需要重新导入后切换来源。" = "仅影响之后复制和重新导入的模块地址;已安装模块需要重新导入后切换来源。";
"验证状态" = "验证状态";
"已复制模块订阅地址" = "已复制模块订阅地址";
"复制模块订阅地址" = "复制模块订阅地址";
"复制 %@" = "复制 %@";
"已复制 %@" = "已复制 %@";
"已复制解密域名" = "已复制解密域名";
"复制解密域名" = "复制解密域名";
"打开 %@" = "打开 %@";
"重新打开配置引导" = "重新打开配置引导";
"Egern 直接使用 Surge 的 .sgmodule 模块。" = "Egern 直接使用 Surge 的 .sgmodule 模块。";
"Stash 直接订阅 .stoverride,不要通过 Script Hub 转换。" = "Stash 直接订阅 .stoverride,不要通过 Script Hub 转换。";
"复制模块订阅地址后,在对应代理客户端中添加模块/重写订阅,并为复制的全部域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)启用 MITM。第三方客户端保存坐标后,即使关闭本 App,坐标仍由代理客户端持久化并继续生效。" = "复制模块订阅地址后,在对应代理客户端中添加模块/重写订阅,并为复制的全部域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)启用 MITM。第三方客户端保存坐标后,即使关闭本 App,坐标仍由代理客户端持久化并继续生效。";
"已是最新版本" = "已是最新版本";
"当前版本 %@,远程最新版本 %@。" = "当前版本 %@,远程最新版本 %@。";
"检查更新失败" = "检查更新失败";
"无法获取远程版本信息,请检查网络后重试。" = "无法获取远程版本信息,请检查网络后重试。";
"未检测" = "未检测";
"已连接,有坐标" = "已连接,有坐标";
"已连接,无坐标" = "已连接,无坐标";
"连接失败" = "连接失败";
"已开启" = "已开启";
"已关闭" = "已关闭";
"第三方已保存" = "第三方已保存";
"未保存" = "未保存";
"未知" = "未知";
"App 只负责地图选点、收藏和发送 WGS-84 坐标。第三方代理客户端通过模块拦截 Apple WLOC 请求并持久化当前坐标;本模式不启动本机代理,不使用 App 的 CA,也不需要配置 127.0.0.1:8888。" = "App 只负责地图选点、收藏和发送 WGS-84 坐标。第三方代理客户端通过模块拦截 Apple WLOC 请求并持久化当前坐标;本模式不启动本机代理,不使用 App 的 CA,也不需要配置 127.0.0.1:8888。";
"App 在设备本地运行一个代理服务器(127.0.0.1:8888)。\n\n通过 WiFi 手动代理配置,让系统发往 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的定位请求经过这个本地代理。代理使用已安装的 CA 证书对 HTTPS 流量做中间人解密,把 Apple 返回的定位坐标改写为你设置的虚拟坐标,再加密返回给系统,从而实现虚拟定位。" = "App 在设备本地运行一个代理服务器(127.0.0.1:8888)。\n\n通过 WiFi 手动代理配置,让系统发往 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的定位请求经过这个本地代理。代理使用已安装的 CA 证书对 HTTPS 流量做中间人解密,把 Apple 返回的定位坐标改写为你设置的虚拟坐标,再加密返回给系统,从而实现虚拟定位。";
"模式已切换" = "模式已切换";
"第三方代理模式检测通过。请关闭 Wi-Fi 中的 127.0.0.1:8888 手动代理,避免双重拦截。" = "第三方代理模式检测通过。请关闭 Wi-Fi 中的 127.0.0.1:8888 手动代理,避免双重拦截。";
"APP 模式环境检测通过。请停用第三方 WLOC 模块或代理连接,避免双重拦截。" = "APP 模式环境检测通过。请停用第三方 WLOC 模块或代理连接,避免双重拦截。";
"证书重置失败" = "证书重置失败";
"无法打开客户端" = "无法打开客户端";
"无法打开 %@,请确认客户端已安装后手动打开。" = "无法打开 %@,请确认客户端已安装后手动打开。";
"正在初始化地图与本地代理…" = "正在初始化地图与本地代理…";
"正在初始化地图…" = "正在初始化地图…";
"更新说明暂时无法加载,请前往最新 Release 页面查看。" = "更新说明暂时无法加载,请前往最新 Release 页面查看。";
"需要更新" = "需要更新";
"当前版本 %@ 已停止支持,请更新到 %@ 后继续使用。\n\n%@" = "当前版本 %@ 已停止支持,请更新到 %@ 后继续使用。\n\n%@";
"立即更新" = "立即更新";
"发现新版本" = "发现新版本";
"当前版本 %@,最新版本 %@。\n\n%@" = "当前版本 %@,最新版本 %@。\n\n%@";
"前往更新" = "前往更新";
"稍后" = "稍后";
"遇到问题时,在这里生成 Bug 报告。系统会运行一次诊断测试,并将完整报告复制到剪切板。跳转到 GitHub 后,请粘贴到“App 生成的诊断报告”字段。" = "遇到问题时,在这里生成 Bug 报告。系统会运行一次诊断测试,并将完整报告复制到剪切板。跳转到 GitHub 后,请粘贴到“App 生成的诊断报告”字段。";
"可复现环境" = "可复现环境";
"当前设备上问题稳定复现,非偶发性。" = "当前设备上问题稳定复现,非偶发性。";
"问题描述" = "问题描述";
"正在生成报告…" = "正在生成报告…";
"生成 Bug 报告" = "生成 Bug 报告";
"报告 Bug" = "报告 Bug";
"已生成" = "已生成";
"打开 GitHub 表单" = "打开 GitHub 表单";
"稍后再说" = "稍后再说";
"Bug 报告已复制到剪切板。请在 GitHub 表单的“App 生成的诊断报告”字段中粘贴并提交。" = "Bug 报告已复制到剪切板。请在 GitHub 表单的“App 生成的诊断报告”字段中粘贴并提交。";
"过滤日志" = "过滤日志";
"暂无运行日志" = "暂无运行日志";
"无匹配日志" = "无匹配日志";
"运行日志" = "运行日志";
"日志自动清理,仅保留近 3 天" = "日志自动清理,仅保留近 3 天";
"关闭" = "关闭";
"复制全部日志" = "复制全部日志";
"清空全部日志" = "清空全部日志";
"清空所有运行日志?" = "清空所有运行日志?";
"清空日志" = "清空日志";
"取消" = "取消";
"正在检测…" = "正在检测…";
"环境检测" = "环境检测";
"检查第三方模块能否拦截并响应 query 请求;不会写入测试坐标。" = "检查第三方模块能否拦截并响应 query 请求;不会写入测试坐标。";
"依次检查:本地代理 → CA 证书信任 → Wi-Fi 代理链路。" = "依次检查:本地代理 → CA 证书信任 → Wi-Fi 代理链路。";
"测试日志" = "测试日志";
"已复制" = "已复制";
"代理运行中" = "代理运行中";
"代理未运行" = "代理未运行";
"可修改" = "可修改";
"不可修改" = "不可修改";
"环境检测通过" = "环境检测通过";
"环境检测失败: %@" = "环境检测失败: %@";
",查看下方日志" = ",查看下方日志";
"第三方模块连接通过,已有坐标" = "第三方模块连接通过,已有坐标";
"第三方模块连接通过,暂无坐标" = "第三方模块连接通过,暂无坐标";
"第三方模块连接失败" = "第三方模块连接失败";
"======== 第三方代理连接检测 ========" = "======== 第三方代理连接检测 ========";
"模式: 测试模式" = "模式: 测试模式";
"请求: wloc-settings/save?action=query" = "请求: wloc-settings/save?action=query";
"拦截响应: 有效 JSON" = "拦截响应: 有效 JSON";
"已保存坐标: %@" = "已保存坐标: %@";
"结果: %@" = "结果: %@";
"生效说明" = "生效说明";
"失效说明" = "失效说明";
"关闭 WiFi 代理" = "关闭 WiFi 代理";
"知道了" = "知道了";
"让虚拟定位生效" = "让虚拟定位生效";
"确认第三方代理已开启" = "确认第三方代理已开启";
"保持已导入的 WLOC 模块、HTTPS 解密和第三方代理/VPN 连接开启。" = "保持已导入的 WLOC 模块、HTTPS 解密和第三方代理/VPN 连接开启。";
"开启飞行模式" = "开启飞行模式";
"从控制中心打开飞行模式(点飞机图标),Wi‑Fi 会自动断开。这是为了清除 iOS 的定位缓存。等待 2 秒。" = "从控制中心打开飞行模式(点飞机图标),Wi‑Fi 会自动断开。这是为了清除 iOS 的定位缓存。等待 2 秒。";
"关闭 Wi‑Fi" = "关闭 Wi‑Fi";
"从控制中心再点一下 Wi‑Fi 图标,确认 Wi‑Fi 已关闭。等待 2 秒。" = "从控制中心再点一下 Wi‑Fi 图标,确认 Wi‑Fi 已关闭。等待 2 秒。";
"关闭系统定位服务" = "关闭系统定位服务";
"打开系统「设置 → 隐私与安全性 → 定位服务」,关闭顶部的总开关。等待 2 秒。" = "打开系统「设置 → 隐私与安全性 → 定位服务」,关闭顶部的总开关。等待 2 秒。";
"打开 Wi‑Fi,启动虚拟定位" = "打开 Wi‑Fi,启动虚拟定位";
"从控制中心打开 Wi‑Fi(飞行模式保持开启),确认第三方代理已连接。坐标已经同步到第三方代理。等待 2 秒。" = "从控制中心打开 Wi‑Fi(飞行模式保持开启),确认第三方代理已连接。坐标已经同步到第三方代理。等待 2 秒。";
"从控制中心打开 Wi‑Fi(飞行模式保持开启),进入 App 点底部「开始虚拟定位」。等待 2 秒。" = "从控制中心打开 Wi‑Fi(飞行模式保持开启),进入 App 点底部「开始虚拟定位」。等待 2 秒。";
"关闭飞行模式" = "关闭飞行模式";
"从控制中心关闭飞行模式。等待 2 秒。" = "从控制中心关闭飞行模式。等待 2 秒。";
"重新开启定位服务" = "重新开启定位服务";
"再次进入「设置 → 隐私与安全性 → 定位服务」,打开总开关。完成后打开地图验证定位是否已变化。" = "再次进入「设置 → 隐私与安全性 → 定位服务」,打开总开关。完成后打开地图验证定位是否已变化。";
"还是无法生效?" = "还是无法生效?";
"操作到第 3 步时关机重启,开机后从第 4 步继续。这样能彻底清除系统缓存的定位数据。" = "操作到第 3 步时关机重启,开机后从第 4 步继续。这样能彻底清除系统缓存的定位数据。";
"去设置" = "去设置";
"取消虚拟定位" = "取消虚拟定位";
"从控制中心打开飞行模式,Wi‑Fi 会自动断开。等待 2 秒。" = "从控制中心打开飞行模式,Wi‑Fi 会自动断开。等待 2 秒。";
"从控制中心确认 Wi‑Fi 已关闭。等待 2 秒。" = "从控制中心确认 Wi‑Fi 已关闭。等待 2 秒。";
"打开「设置 → 隐私与安全性 → 定位服务」,关闭总开关。等待 2 秒。" = "打开「设置 → 隐私与安全性 → 定位服务」,关闭总开关。等待 2 秒。";
"确认坐标已清除" = "确认坐标已清除";
"App 已通知第三方代理清除虚拟坐标。保持网络可用并等待 2 秒,让系统重新获取真实定位。" = "App 已通知第三方代理清除虚拟坐标。保持网络可用并等待 2 秒,让系统重新获取真实定位。";
"打开 Wi‑Fi,移除代理" = "打开 Wi‑Fi,移除代理";
"从控制中心打开 Wi‑Fi。然后进入「设置 → 无线局域网 → 点 WiFi 右侧 (i) → HTTP 代理」,选择「关闭」后存储。等待 2 秒。" = "从控制中心打开 Wi‑Fi。然后进入「设置 → 无线局域网 → 点 WiFi 右侧 (i) → HTTP 代理」,选择「关闭」后存储。等待 2 秒。";
"再次进入「设置 → 隐私与安全性 → 定位服务」打开总开关。打开地图验证定位是否恢复。" = "再次进入「设置 → 隐私与安全性 → 定位服务」打开总开关。打开地图验证定位是否恢复。";
"还是无法取消?" = "还是无法取消?";
"操作到第 3 步时关机重启,开机后从第 4 步继续。" = "操作到第 3 步时关机重启,开机后从第 4 步继续。";
"移除代理配置" = "移除代理配置";
"停止虚拟定位后,需要手动移除 WiFi 代理配置,否则可能无法上网。\n\n1. 打开「设置 → 无线局域网」\n2. 点击当前 WiFi 右侧 (i) 图标\n3. 找到「HTTP 代理」\n4. 选择「关闭」\n5. 点右上角「存储」" = "停止虚拟定位后,需要手动移除 WiFi 代理配置,否则可能无法上网。\n\n1. 打开「设置 → 无线局域网」\n2. 点击当前 WiFi 右侧 (i) 图标\n3. 找到「HTTP 代理」\n4. 选择「关闭」\n5. 点右上角「存储」";
"社区分享成功配置?" = "社区分享成功配置?";
"去提交" = "去提交";
"复制模板" = "复制模板";
"不再提示" = "不再提示";
"第三方客户端" = "第三方客户端";
"你正在使用 %@。点击“去提交”会先复制投稿模板,并在 App 内打开社区页面。采纳后将收录到 README,可选择是否匿名署名。" = "你正在使用 %@。点击“去提交”会先复制投稿模板,并在 App 内打开社区页面。采纳后将收录到 README,可选择是否匿名署名。";
"已复制投稿模板" = "已复制投稿模板";
"如果 GitHub 登录或浏览器跳转后模板没有自动填充,可以直接粘贴。" = "如果 GitHub 登录或浏览器跳转后模板没有自动填充,可以直接粘贴。";
"无法直接跳转" = "无法直接跳转";
"定位失败" = "定位失败";
"打开设置" = "打开设置";
"无法获取当前定位,请检查定位服务是否已开启" = "无法获取当前定位,请检查定位服务是否已开启";
"编辑收藏名称" = "编辑收藏名称";
"名称" = "名称";
"保存" = "保存";
"修改收藏地点名称" = "修改收藏地点名称";
"搜索地点或坐标" = "搜索地点或坐标";
"日志" = "日志";
"更多" = "更多";
"当前选点" = "当前选点";
"GCJ-02(国内)" = "GCJ-02(国内)";
"WGS-84(国际)" = "WGS-84(国际)";
"无法生效?" = "无法生效?";
"无法取消?" = "无法取消?";
"已收藏,点击取消收藏" = "已收藏,点击取消收藏";
"收藏当前选点" = "收藏当前选点";
"搜索或点击地图选点后,保存为收藏。" = "搜索或点击地图选点后,保存为收藏。";
"切换到此处" = "切换到此处";
"同步到第三方代理" = "同步到第三方代理";
"检测并同步中…" = "检测并同步中…";
"停止第三方虚拟定位" = "停止第三方虚拟定位";
"开始虚拟定位" = "开始虚拟定位";
"验证环境中…" = "验证环境中…";
"停止虚拟定位" = "停止虚拟定位";
"当前未连接可用的 Wi-Fi,请连接 Wi-Fi 后配置 127.0.0.1:8888 手动代理。" = "当前未连接可用的 Wi-Fi,请连接 Wi-Fi 后配置 127.0.0.1:8888 手动代理。";
"第三方代理查询失败" = "第三方代理查询失败";
"未命名" = "未命名";
"没有找到相关地点" = "没有找到相关地点";
"虚拟定位已开启" = "虚拟定位已开启";
"不再提醒" = "不再提醒";
"虚拟定位已关闭" = "虚拟定位已关闭";
"选择模式" = "选择模式";
"配置 Wi-Fi 代理" = "配置 Wi-Fi 代理";
"初始化 CA 证书" = "初始化 CA 证书";
"选择客户端" = "选择客户端";
"导入并检测" = "导入并检测";
"上一步" = "上一步";
"开始使用" = "开始使用";
"操作失败" = "操作失败";
"选择运行模式" = "选择运行模式";
"后续可在“设置 → 运行模式”中切换。两种模式不要同时拦截 WLOC 请求。" = "后续可在“设置 → 运行模式”中切换。两种模式不要同时拦截 WLOC 请求。";
"仅 Wi-Fi" = "仅 Wi-Fi";
"无外部依赖" = "无外部依赖";
"App 在设备本地启动代理,通过当前 Wi-Fi 的手动 HTTP 代理改写定位响应。免费自签应用无法使用系统 VPN 的 Network Extension 能力,因此 APP模式不支持蜂窝网络,需要配置 Wi-Fi 代理并安装 App 生成的 CA。" = "App 在设备本地启动代理,通过当前 Wi-Fi 的手动 HTTP 代理改写定位响应。免费自签应用无法使用系统 VPN 的 Network Extension 能力,因此 APP模式不支持蜂窝网络,需要配置 Wi-Fi 代理并安装 App 生成的 CA。";
"测试模式" = "测试模式";
"App 负责选点,并通过 WLOC 配置接口查询和同步坐标;第三方代理客户端负责网络代理、模块拦截、MITM 和持久化。证书、VPN 与代理连接均由第三方客户端处理。" = "App 负责选点,并通过 WLOC 配置接口查询和同步坐标;第三方代理客户端负责网络代理、模块拦截、MITM 和持久化。证书、VPN 与代理连接均由第三方客户端处理。";
"正在准备 APP模式本地服务…" = "正在准备 APP模式本地服务…";
"先配置 Wi-Fi 系统代理" = "先配置 Wi-Fi 系统代理";
"在当前 Wi-Fi 的详情页,将「HTTP 代理」设为「手动」:服务器填 127.0.0.1,端口填 8888。配置后点击下方「完成」。检测会自动判断是 Wi-Fi 代理还是证书信任有问题。" = "在当前 Wi-Fi 的详情页,将「HTTP 代理」设为「手动」:服务器填 127.0.0.1,端口填 8888。配置后点击下方「完成」。检测会自动判断是 Wi-Fi 代理还是证书信任有问题。";
"Wi-Fi 代理设置" = "Wi-Fi 代理设置";
"1 选择手动,2 填写服务器 127.0.0.1,3 填写端口 8888。" = "1 选择手动,2 填写服务器 127.0.0.1,3 填写端口 8888。";
"复制地址" = "复制地址";
"打开 Wi-Fi 设置" = "打开 Wi-Fi 设置";
"第 1 步:下载证书" = "第 1 步:下载证书";
"下载本机随机生成的 CA 根证书。私钥仅保存在此设备的钥匙串中,不会随证书文件导出。App 会弹出 Safari 下载页;出现配置描述文件下载提示时,选择「允许」。" = "下载本机随机生成的 CA 根证书。私钥仅保存在此设备的钥匙串中,不会随证书文件导出。App 会弹出 Safari 下载页;出现配置描述文件下载提示时,选择「允许」。";
"打开下载页" = "打开下载页";
"无法准备证书下载页面,请查看诊断日志" = "无法准备证书下载页面,请查看诊断日志";
"第 2 步:安装证书" = "第 2 步:安装证书";
"下载完成后打开系统「设置」。如果顶部显示「已下载描述文件」,点进去安装;否则进入「通用 → VPN 与设备管理」,找到 Location Spoofer CA 并完成安装。" = "下载完成后打开系统「设置」。如果顶部显示「已下载描述文件」,点进去安装;否则进入「通用 → VPN 与设备管理」,找到 Location Spoofer CA 并完成安装。";
"去安装" = "去安装";
"安装证书" = "安装证书";
"1 在「VPN 与设备管理」中打开 Location Spoofer CA 描述文件并完成安装。" = "1 在「VPN 与设备管理」中打开 Location Spoofer CA 描述文件并完成安装。";
"第 3 步:信任证书" = "第 3 步:信任证书";
"安装后进入「设置 → 通用 → 关于本机 → 证书信任设置」,找到 Location Spoofer CA 并开启完全信任。iOS 保留钥匙串数据时,重装 App 会继续复用同一证书。" = "安装后进入「设置 → 通用 → 关于本机 → 证书信任设置」,找到 Location Spoofer CA 并开启完全信任。iOS 保留钥匙串数据时,重装 App 会继续复用同一证书。";
"去信任" = "去信任";
"信任证书" = "信任证书";
"1 在「证书信任设置」中为 Location Spoofer CA 开启完全信任。" = "1 在「证书信任设置」中为 Location Spoofer CA 开启完全信任。";
"已完成" = "已完成";
"已完成 ✓" = "已完成 ✓";
"iOS 27 beta 6 起,系统已禁止对 gs-loc.apple.com 进行 MITM 拦截。该版本及之后的 beta 版本暂时无法使用本项目,等待后续适配方案。" = "iOS 27 beta 6 起,系统已禁止对 gs-loc.apple.com 进行 MITM 拦截。该版本及之后的 beta 版本暂时无法使用本项目,等待后续适配方案。";
"选择第三方代理客户端" = "选择第三方代理客户端";
"除 Shadowrocket 外,当前客户端配置尚未完成真机验证,页面只提供模块导入入口和通用配置提醒。" = "除 Shadowrocket 外,当前客户端配置尚未完成真机验证,页面只提供模块导入入口和通用配置提醒。";
"第三方客户端适配说明" = "第三方客户端适配说明";
"App 不连接远程坐标服务器,而是向 Apple 域名发起一个约定请求。第三方客户端需要在本机拦截该请求、保存 WGS-84 坐标并返回 JSON;定位模块再读取同一份数据,修改 Apple WLOC 响应。" = "App 不连接远程坐标服务器,而是向 Apple 域名发起一个约定请求。第三方客户端需要在本机拦截该请求、保存 WGS-84 坐标并返回 JSON;定位模块再读取同一份数据,修改 Apple WLOC 响应。";
"配置接口" = "配置接口";
"查询:GET ?action=query\n保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>\n清除:GET ?action=clear" = "查询:GET ?action=query\n保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>\n清除:GET ?action=clear";
"返回格式" = "返回格式";
"适配要求" = "适配要求";
"客户端需要支持请求脚本、持久化存储、HTTP 200 JSON 响应、Apple WLOC 响应脚本,以及 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的 HTTPS 解密。保存接口和 WLOC 响应脚本必须读取同一份持久化数据。" = "客户端需要支持请求脚本、持久化存储、HTTP 200 JSON 响应、Apple WLOC 响应脚本,以及 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的 HTTPS 解密。保存接口和 WLOC 响应脚本必须读取同一份持久化数据。";
"检测到第三方代理连接异常,请检查模块、MITM 和代理连接后重新检测。" = "检测到第三方代理连接异常,请检查模块、MITM 和代理连接后重新检测。";
"第 1 步:导入 %@ 模块" = "第 1 步:导入 %@ 模块";
"复制 %@ 的模块订阅地址。" = "复制 %@ 的模块订阅地址。";
"打开 Shadowrocket,进入“配置 → 模块”。" = "打开 Shadowrocket,进入“配置 → 模块”。";
"打开 %@。" = "打开 %@。";
"进入 Shadowrocket 配置" = "进入 Shadowrocket 配置";
"1 点击「模块」进入模块列表,2 可打开当前本地配置详情。" = "1 点击「模块」进入模块列表,2 可打开当前本地配置详情。";
"点击右上角“+”,粘贴模块订阅地址并导入,然后确认模块已启用。" = "点击右上角“+”,粘贴模块订阅地址并导入,然后确认模块已启用。";
"导入 Shadowrocket 模块" = "导入 Shadowrocket 模块";
"1 点击右上角加号导入模块,2 确认模块已启用。" = "1 点击右上角加号导入模块,2 确认模块已启用。";
"在 %@ 中导入刚才复制的模块订阅地址。" = "在 %@ 中导入刚才复制的模块订阅地址。";
"第 2 步:完成 %@ 配置" = "第 2 步:完成 %@ 配置";
"请在 %@ 中完成相应配置。" = "请在 %@ 中完成相应配置。";
"配置时请复制下方全部解密域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)。" = "配置时请复制下方全部解密域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)。";
"第 2 步:配置 HTTPS 解密" = "第 2 步:配置 HTTPS 解密";
"进入“配置 → 本地文件”,找到带黄点的配置,点击右侧 i 图标。" = "进入“配置 → 本地文件”,找到带黄点的配置,点击右侧 i 图标。";
"进入“HTTPS 解密”,开启解密开关。" = "进入“HTTPS 解密”,开启解密开关。";
"在域名列表中添加下方复制的全部解密域名。" = "在域名列表中添加下方复制的全部解密域名。";
"配置 HTTPS 解密" = "配置 HTTPS 解密";
"1 开启 HTTPS 解密,2 添加复制的全部解密域名,3 打开证书设置。" = "1 开启 HTTPS 解密,2 添加复制的全部解密域名,3 打开证书设置。";
"按 Shadowrocket 提示生成并完成证书授权。" = "按 Shadowrocket 提示生成并完成证书授权。";
"授权 Shadowrocket 证书" = "授权 Shadowrocket 证书";
"1 打开 Shadowrocket 证书项并按提示安装、授权。" = "1 打开 Shadowrocket 证书项并按提示安装、授权。";
"返回 HTTPS 解密页面,点击右上角勾号保存,然后开启代理。" = "返回 HTTPS 解密页面,点击右上角勾号保存,然后开启代理。";
"打开 Shadowrocket 继续配置" = "打开 Shadowrocket 继续配置";
"App 只能唤起 Shadowrocket,无法通过公开接口直接跳转到“模块”或“HTTPS 解密”页面。" = "App 只能唤起 Shadowrocket,无法通过公开接口直接跳转到“模块”或“HTTPS 解密”页面。";
"轻点查看大图" = "轻点查看大图";
"证书尚未安装或信任" = "证书尚未安装或信任";
"Wi-Fi 代理未正确设置" = "Wi-Fi 代理未正确设置";
"本地代理未能启动" = "本地代理未能启动";
"检测仍在进行" = "检测仍在进行";
"检测结果已过期" = "检测结果已过期";
"定位改写检测失败" = "定位改写检测失败";
"接口连接失败" = "接口连接失败";
"本地代理初始化失败:%@" = "本地代理初始化失败:%@";
"✓ 定位环境正常" = "✓ 定位环境正常";
"CA 证书未安装或未信任" = "CA 证书未安装或未信任";
"Wi-Fi 代理未正确设置,请检查 127.0.0.1:8888" = "Wi-Fi 代理未正确设置,请检查 127.0.0.1:8888";
"请手动打开「设置」,找到本 App 后检查定位权限。" = "请手动打开「设置」,找到本 App 后检查定位权限。";
"请手动打开「设置 → 通用」。" = "请手动打开「设置 → 通用」。";
"请手动打开「设置 → 无线局域网」,进入当前 Wi-Fi 的详情页。" = "请手动打开「设置 → 无线局域网」,进入当前 Wi-Fi 的详情页。";
"请手动打开「设置 → 隐私与安全性 → 定位服务」。" = "请手动打开「设置 → 隐私与安全性 → 定位服务」。";
"第三方代理返回了无法识别的数据" = "第三方代理返回了无法识别的数据";
"请求未被第三方代理模块拦截,请检查模块、MITM 和代理连接" = "请求未被第三方代理模块拦截,请检查模块、MITM 和代理连接";
"第三方代理保存的坐标与当前选点不一致" = "第三方代理保存的坐标与当前选点不一致";
"第三方代理请求失败:%@" = "第三方代理请求失败:%@";
"检查模块、MITM、证书和代理/VPN连接" = "检查模块、MITM、证书和代理/VPN连接";
"第三方代理拒绝保存坐标" = "第三方代理拒绝保存坐标";
"第三方代理清除坐标失败" = "第三方代理清除坐标失败";
"已有第三方代理请求正在执行" = "已有第三方代理请求正在执行";
"配置已提供,尚未验证" = "配置已提供,尚未验证";
"已连接,有保存坐标" = "已连接,有保存坐标";
"已连接,无保存坐标" = "已连接,无保存坐标";
"连接失败(%@)" = "连接失败(%@)";
"当前客户端:%@" = "当前客户端:%@";
"配置接口:/wloc-settings/save" = "配置接口:/wloc-settings/save";
"请求动作:WLOC query" = "请求动作:WLOC query";
"检查范围:模块拦截、MITM、证书、代理/VPN 连接" = "检查范围:模块拦截、MITM、证书、代理/VPN 连接";
"连接状态:%@" = "连接状态:%@";
"检测结果:失败" = "检测结果:失败";
"耗时:%lld ms" = "耗时:%lld ms";
"错误类型:%@" = "错误类型:%@";
"错误详情:%@" = "错误详情:%@";
"处理建议:%@。" = "处理建议:%@。";
"======== 第三方代理运行检测 ========" = "======== 第三方代理运行检测 ========";
"触发来源:地图或设置中的第三方代理操作" = "触发来源:地图或设置中的第三方代理操作";
"请求动作:WLOC 配置接口" = "请求动作:WLOC 配置接口";
"处理建议:确认模块已启用,并检查 MITM、证书和代理/VPN 连接。" = "处理建议:确认模块已启用,并检查 MITM、证书和代理/VPN 连接。";
"环境信息" = "环境信息";
"App 版本" = "App 版本";
"系统版本" = "系统版本";
"诊断日志" = "诊断日志";
"不适用" = "不适用";
"是" = "是";
"否" = "否";
"(无诊断数据)" = "(无诊断数据)";
"第三方代理测试模式:模块连接成功;已保存坐标=%@" = "第三方代理测试模式:模块连接成功;已保存坐标=%@";
"第三方代理测试模式:模块连接失败;%@" = "第三方代理测试模式:模块连接失败;%@";
"======== 代理验证测试 ========" = "======== 代理验证测试 ========";
"App 版本: %@" = "App 版本: %@";
"系统版本: iOS %@" = "系统版本: iOS %@";
"[步骤 A] 检查代理是否运行…" = "[步骤 A] 检查代理是否运行…";
" 端口: 127.0.0.1:8888" = " 端口: 127.0.0.1:8888";
" ⚠ 代理未运行,尝试启动…" = " ⚠ 代理未运行,尝试启动…";
" ✗ 启动失败: %@" = " ✗ 启动失败: %@";
" ✓ 代理启动成功" = " ✓ 代理启动成功";
" ✓ 代理已在运行中" = " ✓ 代理已在运行中";
"[步骤 B] 检测证书与 WiFi 代理…" = "[步骤 B] 检测证书与 WiFi 代理…";
" 方式: 请求 baidu.com/paopao-verify-<token>" = " 方式: 请求 baidu.com/paopao-verify-<token>";
" 结果判定: TLS 错误=证书问题 / 响应不匹配=代理未配置 / 匹配=通过" = " 结果判定: TLS 错误=证书问题 / 响应不匹配=代理未配置 / 匹配=通过";
" ✗ 无法生成验证 token" = " ✗ 无法生成验证 token";
" ✓ 证书已信任,WiFi 代理已配置" = " ✓ 证书已信任,WiFi 代理已配置";
" ✗ 响应不匹配: HTTP %lld, %lld bytes,WiFi 代理未配置" = " ✗ 响应不匹配: HTTP %lld, %lld bytes,WiFi 代理未配置";
" ✗ 请求失败 [%@ code=%lld]: %@" = " ✗ 请求失败 [%@ code=%lld]: %@";
" TLS/证书校验失败,CA 证书未信任" = " TLS/证书校验失败,CA 证书未信任";
"======== 环境检测通过 ✓ ========" = "======== 环境检测通过 ✓ ========";
" --- 代理日志 ---" = " --- 代理日志 ---";
"成功:{\"success\":true,\"longitude\":113.0,\"latitude\":22.0,\"accuracy\":25}\n失败:{\"success\":false,\"error\":\"错误说明\"}" = "成功:{\"success\":true,\"longitude\":113.0,\"latitude\":22.0,\"accuracy\":25}\n失败:{\"success\":false,\"error\":\"错误说明\"}";
"定位改写脚本源自 Yu9191/wloc,现由本项目维护" = "定位改写脚本源自 Yu9191/wloc,现由本项目维护";
"地图" = "地图";
"坐标转换" = "坐标转换";
"定位" = "定位";
"实时定位" = "实时定位";
"提醒" = "提醒";
"搜索" = "搜索";
"收藏" = "收藏";
"缩放" = "缩放";
"========== App 启动 ==========" = "========== App 启动 ==========";
"尚未选择运行模式,跳过本地 CA 和代理初始化" = "尚未选择运行模式,跳过本地 CA 和代理初始化";
"第三方代理测试模式:跳过本地 CA、代理和环境检测" = "第三方代理测试模式:跳过本地 CA、代理和环境检测";
"旧坐标数据迁移失败,将在下次启动重试" = "旧坐标数据迁移失败,将在下次启动重试";
"地图坐标标准初始化完成,开始后续启动流程" = "地图坐标标准初始化完成,开始后续启动流程";
"没有持久化图钉,地图创建前请求实时定位" = "没有持久化图钉,地图创建前请求实时定位";
"已使用实时定位准备唯一初始地图状态" = "已使用实时定位准备唯一初始地图状态";
"地图创建前取得的初始实时位置(WGS-84)" = "地图创建前取得的初始实时位置(WGS-84)";
"地图创建前无法取得实时定位,唯一初始位置使用深圳" = "地图创建前无法取得实时定位,唯一初始位置使用深圳";
"已找到持久化图钉,直接准备唯一初始地图状态" = "已找到持久化图钉,直接准备唯一初始地图状态";
"启动门禁全部完成,现在创建 MapHomeView" = "启动门禁全部完成,现在创建 MapHomeView";
"初始化" = "初始化";
"启动代理 127.0.0.1:8888" = "启动代理 127.0.0.1:8888";
"启动代理: 恢复上次 WGS-84 定位" = "启动代理: 恢复上次 WGS-84 定位";
"启动成功" = "启动成功";
"启动失败" = "启动失败";
"写入坐标" = "写入坐标";
"跳过过期坐标写入" = "跳过过期坐标写入";
"跳过旧验证坐标恢复" = "跳过旧验证坐标恢复";
"运动状态模拟设置已更新" = "运动状态模拟设置已更新";
"准备证书下载失败" = "准备证书下载失败";
"设置虚拟定位坐标" = "设置虚拟定位坐标";
"apply失败" = "apply失败";
"保存收藏失败" = "保存收藏失败";
"本地服务初始化失败" = "本地服务初始化失败";
"代理运行模式已切换" = "代理运行模式已切换";
"已迁移旧版模式初始化状态" = "已迁移旧版模式初始化状态";
"地图坐标标准检测已有请求进行中" = "地图坐标标准检测已有请求进行中";
"地图坐标标准检测开始" = "地图坐标标准检测开始";
"地图坐标标准检测获得明确结果" = "地图坐标标准检测获得明确结果";
"地图坐标标准检测不可用,开始实时定位兜底" = "地图坐标标准检测不可用,开始实时定位兜底";
"地图坐标标准检测使用兜底结果" = "地图坐标标准检测使用兜底结果";
"地图坐标标准检测被取消,保留默认国内标准" = "地图坐标标准检测被取消,保留默认国内标准";
"地图坐标标准已确定,允许创建地图" = "地图坐标标准已确定,允许创建地图";
"地图坐标标准运行期检测合并到进行中请求" = "地图坐标标准运行期检测合并到进行中请求";
"地图坐标标准运行期检测开始" = "地图坐标标准运行期检测开始";
"地图坐标标准运行期检测结果已过期,取消写入" = "地图坐标标准运行期检测结果已过期,取消写入";
"地图坐标标准运行期检测完成,标准未变化" = "地图坐标标准运行期检测完成,标准未变化";
"地图坐标标准运行期检测发现切换" = "地图坐标标准运行期检测发现切换";
"地图坐标标准运行期检测失败,保留当前标准" = "地图坐标标准运行期检测失败,保留当前标准";
"地图坐标标准运行期检测已取消" = "地图坐标标准运行期检测已取消";
"实时定位不覆盖固定锚点的明确检测结果" = "实时定位不覆盖固定锚点的明确检测结果";
"实时定位确认兜底地图坐标标准无需修正" = "实时定位确认兜底地图坐标标准无需修正";
"实时定位修正启动兜底地图坐标标准" = "实时定位修正启动兜底地图坐标标准";
"第三方代理已保存 WGS-84 坐标" = "第三方代理已保存 WGS-84 坐标";
"第三方代理坐标已清除" = "第三方代理坐标已清除";
"第三方代理请求失败" = "第三方代理请求失败";
"第三方代理坐标同步成功" = "第三方代理坐标同步成功";
"同步坐标到第三方客户端失败" = "同步坐标到第三方客户端失败";
"验证结果" = "验证结果";
"验证失败" = "验证失败";
"开启前检测失败,进入对应环境引导" = "开启前检测失败,进入对应环境引导";
"清除第三方客户端坐标失败" = "清除第三方客户端坐标失败";
"累计虚拟定位操作次数" = "累计虚拟定位操作次数";
"已复制坐标" = "已复制坐标";
"地图坐标标准切换时未找到当前选点缓存" = "地图坐标标准切换时未找到当前选点缓存";
"地图坐标标准切换后已使用缓存坐标对回显当前选点" = "地图坐标标准切换后已使用缓存坐标对回显当前选点";
"地图坐标类型已变化" = "地图坐标类型已变化";
"取消保存收藏:检测期间当前选点已变化" = "取消保存收藏:检测期间当前选点已变化";
"保存当前选点为收藏" = "保存当前选点为收藏";
"第三方代理查询返回失败" = "第三方代理查询返回失败";
"启动后第三方代理状态查询失败" = "启动后第三方代理状态查询失败";
"检测到 Wi-Fi 网络变化" = "检测到 Wi-Fi 网络变化";
"网络仍在变化,重新计算环境检测等待时间" = "网络仍在变化,重新计算环境检测等待时间";
"等待 Wi-Fi 连接稳定后检测" = "等待 Wi-Fi 连接稳定后检测";
"延时检测已被更新的网络事件取消" = "延时检测已被更新的网络事件取消";
"稳定等待结束后仍未连接 Wi-Fi,提示检查代理" = "稳定等待结束后仍未连接 Wi-Fi,提示检查代理";
"开始后台环境检测" = "开始后台环境检测";
"已有环境检测运行,1 秒后重试" = "已有环境检测运行,1 秒后重试";
"后台环境检测完成" = "后台环境检测完成";
"环境检测连续被占用,本次不重复弹窗" = "环境检测连续被占用,本次不重复弹窗";
"用户点击实时定位" = "用户点击实时定位";
"MapKit 蓝点尚不可用,启动 CLLocationManager 兜底" = "MapKit 蓝点尚不可用,启动 CLLocationManager 兜底";
"MapKit 蓝点抢先完成请求,取消 CLLocationManager 兜底" = "MapKit 蓝点抢先完成请求,取消 CLLocationManager 兜底";
"虚拟定位开启后的 MapKit 蓝点标准判定" = "虚拟定位开启后的 MapKit 蓝点标准判定";
"登记实时定位请求上下文" = "登记实时定位请求上下文";
"复用进行中的 CLLocationManager 请求并更新意图上下文" = "复用进行中的 CLLocationManager 请求并更新意图上下文";
"CLLocationManager 兜底未返回坐标" = "CLLocationManager 兜底未返回坐标";
"丢弃 CLLocationManager 结果:任务已取消或蓝点已抢先完成" = "丢弃 CLLocationManager 结果:任务已取消或蓝点已抢先完成";
"实时定位坐标完成标准判断并提交到地图" = "实时定位坐标完成标准判断并提交到地图";
"MKLocalSearch 返回地点结果" = "MKLocalSearch 返回地点结果";
"反向地理编码网络失败,准备重试" = "反向地理编码网络失败,准备重试";
"反向地理编码失败" = "反向地理编码失败";
"获得搜索结果" = "获得搜索结果";
"点击收藏点并回显到地图" = "点击收藏点并回显到地图";
"忽略重复 CLLocationManager 请求" = "忽略重复 CLLocationManager 请求";
"CLLocationManager 请求入口" = "CLLocationManager 请求入口";
"授权状态不允许定位" = "授权状态不允许定位";
"创建 CLLocationManager 请求" = "创建 CLLocationManager 请求";
"请求前台定位授权" = "请求前台定位授权";
"定位授权状态变化" = "定位授权状态变化";
"CLLocationManager 返回样本批次" = "CLLocationManager 返回样本批次";
"本批次没有可完成当前请求的样本" = "本批次没有可完成当前请求的样本";
"接受 CLLocationManager 样本并完成请求" = "接受 CLLocationManager 样本并完成请求";
"定位回调失败" = "定位回调失败";
"跳过 CLLocationManager 缓存样本" = "跳过 CLLocationManager 缓存样本";
"等待定位授权超时" = "等待定位授权超时";
"单次定位超时,切换持续定位" = "单次定位超时,切换持续定位";
"持续定位超时" = "持续定位超时";
"开始 CLLocationManager 单次定位" = "开始 CLLocationManager 单次定位";
"开始 CLLocationManager 持续定位兜底" = "开始 CLLocationManager 持续定位兜底";
"CLLocationManager 请求完成" = "CLLocationManager 请求完成";
"CLLocationManager 请求结束但没有坐标" = "CLLocationManager 请求结束但没有坐标";
"使用 CLLocationManager 新鲜缓存" = "使用 CLLocationManager 新鲜缓存";
"实时定位按钮直接使用 MapKit 蓝点缓存" = "实时定位按钮直接使用 MapKit 蓝点缓存";
"主页收到待处理请求所需的 MapKit 蓝点回调" = "主页收到待处理请求所需的 MapKit 蓝点回调";
"主页收到 CLLocationManager 兜底坐标" = "主页收到 CLLocationManager 兜底坐标";
"原始实时定位坐标" = "原始实时定位坐标";
"检查 CLLocationManager 样本" = "检查 CLLocationManager 样本";
"拒绝 CLLocationManager 样本:坐标或精度无效" = "拒绝 CLLocationManager 样本:坐标或精度无效";
"MapKit 蓝点更新但 location 为空" = "MapKit 蓝点更新但 location 为空";
"拒绝 MapKit 蓝点样本:坐标无效" = "拒绝 MapKit 蓝点样本:坐标无效";
"拒绝 MapKit 蓝点样本:水平精度无效" = "拒绝 MapKit 蓝点样本:水平精度无效";
"音频中断恢复" = "音频中断恢复";
"音频会话失败" = "音频会话失败";
"无法创建静音音频缓冲区" = "无法创建静音音频缓冲区";
"引擎启动失败" = "引擎启动失败";
"后台保活已启动(静音音频)" = "后台保活已启动(静音音频)";
"后台保活已停止" = "后台保活已停止";
"保存当前图钉失败" = "保存当前图钉失败";
"存储缩放" = "存储缩放";
"旧坐标数据迁移完成" = "旧坐标数据迁移完成";
"远程版本配置加载成功" = "远程版本配置加载成功";
"版本配置源不可用,尝试下一地址" = "版本配置源不可用,尝试下一地址";
"版本配置加载失败,继续使用内置配置" = "版本配置加载失败,继续使用内置配置";
"版本说明源不可用,尝试下一地址" = "版本说明源不可用,尝试下一地址";
"版本说明加载失败,将使用最新 Release 页面" = "版本说明加载失败,将使用最新 Release 页面";
"复用设备钥匙串中的 CA" = "复用设备钥匙串中的 CA";
"旧 CA 已迁移到设备钥匙串" = "旧 CA 已迁移到设备钥匙串";
"已生成并保存设备专属 CA" = "已生成并保存设备专属 CA";
"已删除设备 CA,等待重新生成" = "已删除设备 CA,等待重新生成";
"钥匙串中的 CA 无效,准备回退" = "钥匙串中的 CA 无效,准备回退";
"删除旧 CA 文件失败,将在下次启动重试" = "删除旧 CA 文件失败,将在下次启动重试";
"无法解析 CA 证书 PEM" = "无法解析 CA 证书 PEM";
"无法创建 SecTrust" = "无法创建 SecTrust";
"SecTrust 评估返回错误" = "SecTrust 评估返回错误";
"CA 证书已被系统信任" = "CA 证书已被系统信任";
"CA 证书未被系统信任" = "CA 证书未被系统信任";
"调用 Go Core 生成 CA" = "调用 Go Core 生成 CA";
"Go Core CA 生成成功" = "Go Core CA 生成成功";
"本地证书服务已在运行" = "本地证书服务已在运行";
"调用 Go Core 启动本地证书服务" = "调用 Go Core 启动本地证书服务";
"本地证书服务启动成功" = "本地证书服务启动成功";
"停止本地证书服务" = "停止本地证书服务";
"开始第三方代理连接检测" = "开始第三方代理连接检测";
"第三方代理连接检测通过" = "第三方代理连接检测通过";
"第三方代理连接检测失败" = "第三方代理连接检测失败";
"关闭代理前已同步关闭虚拟定位" = "关闭代理前已同步关闭虚拟定位";
"切换 APP 模式前无法清除第三方坐标" = "切换 APP 模式前无法清除第三方坐标";
"设置页第三方连接检测通过" = "设置页第三方连接检测通过";
"设置页第三方连接检测失败" = "设置页第三方连接检测失败";
"地图实际显示坐标" = "地图实际显示坐标";
"App回到前台" = "App回到前台";
"App已确认地图标准" = "App已确认地图标准";
"CLLocationManager请求中" = "CLLocationManager请求中";
"Go proxy 启动失败" = "Go proxy 启动失败";
"MapKit地图蓝点" = "MapKit地图蓝点";
"MapKit蓝点新样本" = "MapKit蓝点新样本";
"MapKit蓝点缓存" = "MapKit蓝点缓存";
"SSID可读取" = "SSID可读取";
"WLOC写入标准" = "WLOC写入标准";
"WLOC目标标准" = "WLOC目标标准";
"Wi-Fi接口" = "Wi-Fi接口";
"intent选点revision" = "intent选点revision";
"manager请求中" = "manager请求中";
"上下文存在" = "上下文存在";
"事件原因" = "事件原因";
"任务已取消" = "任务已取消";
"任务已存在" = "任务已存在";
"使用兜底" = "使用兜底";
"保存收藏" = "保存收藏";
"保存数据包含" = "保存数据包含";
"保持未启用" = "保持未启用";
"保留原第三方坐标" = "保留原第三方坐标";
"保留已启用状态" = "保留已启用状态";
"保留标准" = "保留标准";
"修正兜底标准" = "修正兜底标准";
"内存缓存存在" = "内存缓存存在";
"初始坐标来源" = "初始坐标来源";
"初始来源" = "初始来源";
"初始阶段" = "初始阶段";
"判定规则" = "判定规则";
"原兜底来源" = "原兜底来源";
"原因" = "原因";
"取值字段" = "取值字段";
"可显示不再提醒" = "可显示不再提醒";
"启动代理…" = "启动代理…";
"命中林士街" = "命中林士街";
"固定锚点" = "固定锚点";
"固定锚点查询超过5秒" = "固定锚点查询超过5秒";
"固定锚点查询返回空结果" = "固定锚点查询返回空结果";
"国内标准(GCJ-02)" = "国内标准(GCJ-02)";
"国内转换区域" = "国内转换区域";
"国外非转换区域" = "国外非转换区域";
"国际标准(WGS-84)" = "国际标准(WGS-84)";
"国际标准(WGS-84)+国内标准(GCJ-02)" = "国际标准(WGS-84)+国内标准(GCJ-02)";
"图钉已按新类型重设" = "图钉已按新类型重设";
"地图取值字段" = "地图取值字段";
"地图标准" = "地图标准";
"坐标有效" = "坐标有效";
"坐标标准" = "坐标标准";
"垂直精度米" = "垂直精度米";
"基础校验通过" = "基础校验通过";
"处理建议" = "处理建议";
"失败时提示" = "失败时提示";
"实时定位存在" = "实时定位存在";
"实时定位服务区域" = "实时定位服务区域";
"客户端模式" = "客户端模式";
"尝试" = "尝试";
"已取消位置更新" = "已取消位置更新";
"已恢复真实定位" = "已恢复真实定位";
"已清理" = "已清理";
"已重置" = "已重置";
"开启" = "开启";
"异步地理编码" = "异步地理编码";
"当前地图标准" = "当前地图标准";
"当前客户端" = "当前客户端";
"当前标准" = "当前标准";
"当前选点revision" = "当前选点revision";
"恢复状态" = "恢复状态";
"批次索引" = "批次索引";
"持久化字段" = "持久化字段";
"授权状态" = "授权状态";
"授权状态rawValue" = "授权状态rawValue";
"探测失败原因" = "探测失败原因";
"搜索结果" = "搜索结果";
"操作" = "操作";
"新类型" = "新类型";
"无已保存" = "无已保存";
"无法判定" = "无法判定";
"无法启动本地证书服务" = "无法启动本地证书服务";
"无法生成本地证书" = "无法生成本地证书";
"日志策略" = "日志策略";
"旧类型" = "旧类型";
"显示坐标字段" = "显示坐标字段";
"最低版本" = "最低版本";
"最新版本" = "最新版本";
"最终标准" = "最终标准";
"最终阶段" = "最终阶段";
"有坐标" = "有坐标";
"有效样本数" = "有效样本数";
"有缓存" = "有缓存";
"未知错误" = "未知错误";
"本地 CA 证书或私钥无效" = "本地 CA 证书或私钥无效";
"无法写入设备钥匙串(%d)" = "无法写入设备钥匙串(%d)";
"来源" = "来源";
"来源类型" = "来源类型";
"林士街" = "林士街";
"查看诊断日志" = "查看诊断日志";
"样本年龄秒" = "样本年龄秒";
"样本数" = "样本数";
"样本时间" = "样本时间";
"检查范围" = "检查范围";
"检测前标准" = "检测前标准";
"模块拦截、MITM、代理/VPN连接" = "模块拦截、MITM、代理/VPN连接";
"次数" = "次数";
"每次开启首次或判定变化" = "每次开启首次或判定变化";
"水平精度米" = "水平精度米";
"活动requestID" = "活动requestID";
"活动阶段" = "活动阶段";
"海拔米" = "海拔米";
"满足当前请求时间窗" = "满足当前请求时间窗";
"点击实时定位" = "点击实时定位";
"目标所在区域" = "目标所在区域";
"确认标准" = "确认标准";
"社区征集客户端数" = "社区征集客户端数";
"等待秒数" = "等待秒数";
"系统缓存存在" = "系统缓存存在";
"结果" = "结果";
"结果数" = "结果数";
"结果来源" = "结果来源";
"缓存" = "缓存";
"缓存上限秒" = "缓存上限秒";
"缓存时效通过" = "缓存时效通过";
"缩放米" = "缩放米";
"网络可用" = "网络可用";
"耗时毫秒" = "耗时毫秒";
"蓝点回调更接近" = "蓝点回调更接近";
"蓝点缓存" = "蓝点缓存";
"蓝点缓存存在" = "蓝点缓存存在";
"蓝点距GCJ目标米" = "蓝点距GCJ目标米";
"蓝点距WGS目标米" = "蓝点距WGS目标米";
"触发原因" = "触发原因";
"诊断位置" = "诊断位置";
"请求动作" = "请求动作";
"超时毫秒" = "超时毫秒";
"输入坐标标准" = "输入坐标标准";
"连接状态" = "连接状态";
"选点revision" = "选点revision";
"选点期间发生变化" = "选点期间发生变化";
"错误" = "错误";
"错误类型" = "错误类型";
"锚点" = "锚点";
"阶段" = "阶段";
"首条名称" = "首条名称";
"首条名称=林士街→GCJ-02,否则→WGS-84" = "首条名称=林士街→GCJ-02,否则→WGS-84";
"默认国内标准" = "默认国内标准";
"证书下载地址无效" = "证书下载地址无效";
"启动代理失败: %@" = "启动代理失败: %@";
@@ -0,0 +1,6 @@
/* App display name */
"CFBundleDisplayName" = "Location Spoofer";
/* Location permission prompts */
"NSLocationWhenInUseUsageDescription" = "用於顯示和驗證虛擬定位是否生效";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "用於顯示和驗證虛擬定位是否生效";
+668
View File
@@ -0,0 +1,668 @@
/* Generated from en.lproj/Localizable.strings by Scripts/generate-chinese-localizations.swift. */
"APP模式" = "APP模式";
"第三方代理模式" = "第三方代理模式";
"成功" = "成功";
"已有验证正在进行" = "已有驗證正在進行";
"验证已被新位置取代" = "驗證已被新位置取代";
"证书未信任" = "證書未信任";
"WiFi代理未配置" = "WiFi代理未配置";
"坐标写入失败" = "坐標寫入失敗";
"改写验证失败" = "改寫驗證失敗";
"运行模式" = "運行模式";
"模式" = "模式";
"状态" = "狀態";
"本机代理" = "本機代理";
"第三方模块" = "第三方模塊";
"检测连接" = "檢測連接";
"虚拟定位" = "虛擬定位";
"定位模拟" = "定位模擬";
"运动状态模拟" = "運動狀態模擬";
"实验性功能,默认关闭。开启后会同时模拟定位响应中的运动状态。" = "實驗性功能,默認關閉。開啓後會同時模擬定位響應中的運動狀態。";
"随机扰动" = "隨機擾動";
"开启后,下次同步坐标时会给目标点添加随机偏移(默认半径 50 米),避免位置固定在同一点。" = "開啓後,下次同步坐標時會給目標點添加隨機偏移(默認半徑 50 米),避免位置固定在同一點。";
"说明" = "說明";
"工作原理" = "工作原理";
"应用" = "應用";
"进入引导页" = "進入引導頁";
"正在检查…" = "正在檢查…";
"检查更新" = "檢查更新";
"版本" = "版本";
"证书" = "證書";
"重置证书" = "重置證書";
"仅删除 App 钥匙串中的设备 CA。iOS 中已经安装的旧证书需要在系统设置里手动移除。" = "僅刪除 App 鑰匙串中的設備 CA。iOS 中已經安裝的舊證書需要在系統設置裡手動移除。";
"支持" = "支持";
"使用帮助" = "使用幫助";
"功能建议" = "功能建議";
"分享第三方配置" = "分享第三方配置";
"关于" = "關於";
"如果觉得好用,欢迎去 GitHub 给项目点个 Star" = "如果覺得好用,歡迎去 GitHub 給項目點個 Star";
"致谢" = "致謝";
"核心定位改写逻辑移植自 Yu9191/wloc" = "核心定位改寫邏輯移植自 Yu9191/wloc";
"设置" = "設置";
"完成" = "完成";
"代理操作失败" = "代理操作失敗";
"重置证书?" = "重置證書?";
"重置并生成新证书" = "重置並生成新證書";
"当前虚拟定位和本地代理将停止。App 会删除钥匙串中的设备 CA、立即生成新证书,并打开安装与信任引导。你还需要前往 iOS「设置 → 通用 → VPN 与设备管理」手动删除旧证书,然后重新下载安装并完全信任新证书。" = "當前虛擬定位和本地代理將停止。App 會刪除鑰匙串中的設備 CA、立即生成新證書,並打開安裝與信任引導。你還需要前往 iOS「設置 → 通用 → VPN 與設備管理」手動刪除舊證書,然後重新下載安裝並完全信任新證書。";
"第三方代理配置" = "第三方代理配置";
"客户端" = "客戶端";
"使用国内镜像下载模块" = "使用國內鏡像下載模塊";
"仅影响之后复制和重新导入的模块地址;已安装模块需要重新导入后切换来源。" = "僅影響之後複製和重新導入的模塊地址;已安裝模塊需要重新導入後切換來源。";
"验证状态" = "驗證狀態";
"已复制模块订阅地址" = "已複製模塊訂閱地址";
"复制模块订阅地址" = "複製模塊訂閱地址";
"复制 %@" = "複製 %@";
"已复制 %@" = "已複製 %@";
"已复制解密域名" = "已複製解密域名";
"复制解密域名" = "複製解密域名";
"打开 %@" = "打開 %@";
"重新打开配置引导" = "重新打開配置引導";
"Egern 直接使用 Surge 的 .sgmodule 模块。" = "Egern 直接使用 Surge 的 .sgmodule 模塊。";
"Stash 直接订阅 .stoverride,不要通过 Script Hub 转换。" = "Stash 直接訂閱 .stoverride,不要通過 Script Hub 轉換。";
"复制模块订阅地址后,在对应代理客户端中添加模块/重写订阅,并为复制的全部域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)启用 MITM。第三方客户端保存坐标后,即使关闭本 App,坐标仍由代理客户端持久化并继续生效。" = "複製模塊訂閱地址後,在對應代理客戶端中添加模塊/重寫訂閱,並為複製的全部域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)啓用 MITM。第三方客戶端保存坐標後,即使關閉本 App,坐標仍由代理客戶端持久化並繼續生效。";
"已是最新版本" = "已是最新版本";
"当前版本 %@,远程最新版本 %@。" = "當前版本 %@,遠程最新版本 %@。";
"检查更新失败" = "檢查更新失敗";
"无法获取远程版本信息,请检查网络后重试。" = "無法獲取遠程版本信息,請檢查網絡後重試。";
"未检测" = "未檢測";
"已连接,有坐标" = "已連接,有坐標";
"已连接,无坐标" = "已連接,無坐標";
"连接失败" = "連接失敗";
"已开启" = "已開啓";
"已关闭" = "已關閉";
"第三方已保存" = "第三方已保存";
"未保存" = "未保存";
"未知" = "未知";
"App 只负责地图选点、收藏和发送 WGS-84 坐标。第三方代理客户端通过模块拦截 Apple WLOC 请求并持久化当前坐标;本模式不启动本机代理,不使用 App 的 CA,也不需要配置 127.0.0.1:8888。" = "App 只負責地圖選點、收藏和發送 WGS-84 坐標。第三方代理客戶端通過模塊攔截 Apple WLOC 請求並持久化當前坐標;本模式不啓動本機代理,不使用 App 的 CA,也不需要配置 127.0.0.1:8888。";
"App 在设备本地运行一个代理服务器(127.0.0.1:8888)。\n\n通过 WiFi 手动代理配置,让系统发往 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的定位请求经过这个本地代理。代理使用已安装的 CA 证书对 HTTPS 流量做中间人解密,把 Apple 返回的定位坐标改写为你设置的虚拟坐标,再加密返回给系统,从而实现虚拟定位。" = "App 在設備本地運行一個代理服務器(127.0.0.1:8888)。\n\n通過 WiFi 手動代理配置,讓系統發往 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的定位請求經過這個本地代理。代理使用已安裝的 CA 證書對 HTTPS 流量做中間人解密,把 Apple 返回的定位坐標改寫為你設置的虛擬坐標,再加密返回給系統,從而實現虛擬定位。";
"模式已切换" = "模式已切換";
"第三方代理模式检测通过。请关闭 Wi-Fi 中的 127.0.0.1:8888 手动代理,避免双重拦截。" = "第三方代理模式檢測通過。請關閉 Wi-Fi 中的 127.0.0.1:8888 手動代理,避免雙重攔截。";
"APP 模式环境检测通过。请停用第三方 WLOC 模块或代理连接,避免双重拦截。" = "APP 模式環境檢測通過。請停用第三方 WLOC 模塊或代理連接,避免雙重攔截。";
"证书重置失败" = "證書重置失敗";
"无法打开客户端" = "無法打開客戶端";
"无法打开 %@,请确认客户端已安装后手动打开。" = "無法打開 %@,請確認客戶端已安裝後手動打開。";
"正在初始化地图与本地代理…" = "正在初始化地圖與本地代理…";
"正在初始化地图…" = "正在初始化地圖…";
"更新说明暂时无法加载,请前往最新 Release 页面查看。" = "更新說明暫時無法加載,請前往最新 Release 頁面查看。";
"需要更新" = "需要更新";
"当前版本 %@ 已停止支持,请更新到 %@ 后继续使用。\n\n%@" = "當前版本 %@ 已停止支持,請更新到 %@ 後繼續使用。\n\n%@";
"立即更新" = "立即更新";
"发现新版本" = "發現新版本";
"当前版本 %@,最新版本 %@。\n\n%@" = "當前版本 %@,最新版本 %@。\n\n%@";
"前往更新" = "前往更新";
"稍后" = "稍後";
"遇到问题时,在这里生成 Bug 报告。系统会运行一次诊断测试,并将完整报告复制到剪切板。跳转到 GitHub 后,请粘贴到“App 生成的诊断报告”字段。" = "遇到問題時,在這裡生成 Bug 報告。系統會運行一次診斷測試,並將完整報告複製到剪切板。跳轉到 GitHub 後,請粘貼到“App 生成的診斷報告”字段。";
"可复现环境" = "可復現環境";
"当前设备上问题稳定复现,非偶发性。" = "當前設備上問題穩定復現,非偶發性。";
"问题描述" = "問題描述";
"正在生成报告…" = "正在生成報告…";
"生成 Bug 报告" = "生成 Bug 報告";
"报告 Bug" = "報告 Bug";
"已生成" = "已生成";
"打开 GitHub 表单" = "打開 GitHub 表單";
"稍后再说" = "稍後再說";
"Bug 报告已复制到剪切板。请在 GitHub 表单的“App 生成的诊断报告”字段中粘贴并提交。" = "Bug 報告已複製到剪切板。請在 GitHub 表單的“App 生成的診斷報告”字段中粘貼並提交。";
"过滤日志" = "過濾日誌";
"暂无运行日志" = "暫無運行日誌";
"无匹配日志" = "無匹配日誌";
"运行日志" = "運行日誌";
"日志自动清理,仅保留近 3 天" = "日誌自動清理,僅保留近 3 天";
"关闭" = "關閉";
"复制全部日志" = "複製全部日誌";
"清空全部日志" = "清空全部日誌";
"清空所有运行日志?" = "清空所有運行日誌?";
"清空日志" = "清空日誌";
"取消" = "取消";
"正在检测…" = "正在檢測…";
"环境检测" = "環境檢測";
"检查第三方模块能否拦截并响应 query 请求;不会写入测试坐标。" = "檢查第三方模塊能否攔截並響應 query 請求;不會寫入測試坐標。";
"依次检查:本地代理 → CA 证书信任 → Wi-Fi 代理链路。" = "依次檢查:本地代理 → CA 證書信任 → Wi-Fi 代理鏈路。";
"测试日志" = "測試日誌";
"已复制" = "已複製";
"代理运行中" = "代理運行中";
"代理未运行" = "代理未運行";
"可修改" = "可修改";
"不可修改" = "不可修改";
"环境检测通过" = "環境檢測通過";
"环境检测失败: %@" = "環境檢測失敗: %@";
",查看下方日志" = ",查看下方日誌";
"第三方模块连接通过,已有坐标" = "第三方模塊連接通過,已有坐標";
"第三方模块连接通过,暂无坐标" = "第三方模塊連接通過,暫無坐標";
"第三方模块连接失败" = "第三方模塊連接失敗";
"======== 第三方代理连接检测 ========" = "======== 第三方代理連接檢測 ========";
"模式: 测试模式" = "模式: 測試模式";
"请求: wloc-settings/save?action=query" = "請求: wloc-settings/save?action=query";
"拦截响应: 有效 JSON" = "攔截響應: 有效 JSON";
"已保存坐标: %@" = "已保存坐標: %@";
"结果: %@" = "結果: %@";
"生效说明" = "生效說明";
"失效说明" = "失效說明";
"关闭 WiFi 代理" = "關閉 WiFi 代理";
"知道了" = "知道了";
"让虚拟定位生效" = "讓虛擬定位生效";
"确认第三方代理已开启" = "確認第三方代理已開啓";
"保持已导入的 WLOC 模块、HTTPS 解密和第三方代理/VPN 连接开启。" = "保持已導入的 WLOC 模塊、HTTPS 解密和第三方代理/VPN 連接開啓。";
"开启飞行模式" = "開啓飛行模式";
"从控制中心打开飞行模式(点飞机图标),Wi‑Fi 会自动断开。这是为了清除 iOS 的定位缓存。等待 2 秒。" = "從控制中心打開飛行模式(點飛機圖標),Wi‑Fi 會自動斷開。這是為了清除 iOS 的定位緩存。等待 2 秒。";
"关闭 Wi‑Fi" = "關閉 Wi‑Fi";
"从控制中心再点一下 Wi‑Fi 图标,确认 Wi‑Fi 已关闭。等待 2 秒。" = "從控制中心再點一下 Wi‑Fi 圖標,確認 Wi‑Fi 已關閉。等待 2 秒。";
"关闭系统定位服务" = "關閉系統定位服務";
"打开系统「设置 → 隐私与安全性 → 定位服务」,关闭顶部的总开关。等待 2 秒。" = "打開系統「設置 → 隱私與安全性 → 定位服務」,關閉頂部的總開關。等待 2 秒。";
"打开 Wi‑Fi,启动虚拟定位" = "打開 Wi‑Fi,啓動虛擬定位";
"从控制中心打开 Wi‑Fi(飞行模式保持开启),确认第三方代理已连接。坐标已经同步到第三方代理。等待 2 秒。" = "從控制中心打開 Wi‑Fi(飛行模式保持開啓),確認第三方代理已連接。坐標已經同步到第三方代理。等待 2 秒。";
"从控制中心打开 Wi‑Fi(飞行模式保持开启),进入 App 点底部「开始虚拟定位」。等待 2 秒。" = "從控制中心打開 Wi‑Fi(飛行模式保持開啓),進入 App 點底部「開始虛擬定位」。等待 2 秒。";
"关闭飞行模式" = "關閉飛行模式";
"从控制中心关闭飞行模式。等待 2 秒。" = "從控制中心關閉飛行模式。等待 2 秒。";
"重新开启定位服务" = "重新開啓定位服務";
"再次进入「设置 → 隐私与安全性 → 定位服务」,打开总开关。完成后打开地图验证定位是否已变化。" = "再次進入「設置 → 隱私與安全性 → 定位服務」,打開總開關。完成後打開地圖驗證定位是否已變化。";
"还是无法生效?" = "還是無法生效?";
"操作到第 3 步时关机重启,开机后从第 4 步继续。这样能彻底清除系统缓存的定位数据。" = "操作到第 3 步時關機重啓,開機後從第 4 步繼續。這樣能徹底清除系統緩存的定位數據。";
"去设置" = "去設置";
"取消虚拟定位" = "取消虛擬定位";
"从控制中心打开飞行模式,Wi‑Fi 会自动断开。等待 2 秒。" = "從控制中心打開飛行模式,Wi‑Fi 會自動斷開。等待 2 秒。";
"从控制中心确认 Wi‑Fi 已关闭。等待 2 秒。" = "從控制中心確認 Wi‑Fi 已關閉。等待 2 秒。";
"打开「设置 → 隐私与安全性 → 定位服务」,关闭总开关。等待 2 秒。" = "打開「設置 → 隱私與安全性 → 定位服務」,關閉總開關。等待 2 秒。";
"确认坐标已清除" = "確認坐標已清除";
"App 已通知第三方代理清除虚拟坐标。保持网络可用并等待 2 秒,让系统重新获取真实定位。" = "App 已通知第三方代理清除虛擬坐標。保持網絡可用並等待 2 秒,讓系統重新獲取真實定位。";
"打开 Wi‑Fi,移除代理" = "打開 Wi‑Fi,移除代理";
"从控制中心打开 Wi‑Fi。然后进入「设置 → 无线局域网 → 点 WiFi 右侧 (i) → HTTP 代理」,选择「关闭」后存储。等待 2 秒。" = "從控制中心打開 Wi‑Fi。然後進入「設置 → 無線局域網 → 點 WiFi 右側 (i) → HTTP 代理」,選擇「關閉」後存儲。等待 2 秒。";
"再次进入「设置 → 隐私与安全性 → 定位服务」打开总开关。打开地图验证定位是否恢复。" = "再次進入「設置 → 隱私與安全性 → 定位服務」打開總開關。打開地圖驗證定位是否恢復。";
"还是无法取消?" = "還是無法取消?";
"操作到第 3 步时关机重启,开机后从第 4 步继续。" = "操作到第 3 步時關機重啓,開機後從第 4 步繼續。";
"移除代理配置" = "移除代理配置";
"停止虚拟定位后,需要手动移除 WiFi 代理配置,否则可能无法上网。\n\n1. 打开「设置 → 无线局域网」\n2. 点击当前 WiFi 右侧 (i) 图标\n3. 找到「HTTP 代理」\n4. 选择「关闭」\n5. 点右上角「存储」" = "停止虛擬定位後,需要手動移除 WiFi 代理配置,否則可能無法上網。\n\n1. 打開「設置 → 無線局域網」\n2. 點擊當前 WiFi 右側 (i) 圖標\n3. 找到「HTTP 代理」\n4. 選擇「關閉」\n5. 點右上角「存儲」";
"社区分享成功配置?" = "社區分享成功配置?";
"去提交" = "去提交";
"复制模板" = "複製模板";
"不再提示" = "不再提示";
"第三方客户端" = "第三方客戶端";
"你正在使用 %@。点击“去提交”会先复制投稿模板,并在 App 内打开社区页面。采纳后将收录到 README,可选择是否匿名署名。" = "你正在使用 %@。點擊“去提交”會先複製投稿模板,並在 App 內打開社區頁面。採納後將收錄到 README,可選擇是否匿名署名。";
"已复制投稿模板" = "已複製投稿模板";
"如果 GitHub 登录或浏览器跳转后模板没有自动填充,可以直接粘贴。" = "如果 GitHub 登錄或瀏覽器跳轉後模板沒有自動填充,可以直接粘貼。";
"无法直接跳转" = "無法直接跳轉";
"定位失败" = "定位失敗";
"打开设置" = "打開設置";
"无法获取当前定位,请检查定位服务是否已开启" = "無法獲取當前定位,請檢查定位服務是否已開啓";
"编辑收藏名称" = "編輯收藏名稱";
"名称" = "名稱";
"保存" = "保存";
"修改收藏地点名称" = "修改收藏地點名稱";
"搜索地点或坐标" = "搜索地點或坐標";
"日志" = "日誌";
"更多" = "更多";
"当前选点" = "當前選點";
"GCJ-02(国内)" = "GCJ-02(國內)";
"WGS-84(国际)" = "WGS-84(國際)";
"无法生效?" = "無法生效?";
"无法取消?" = "無法取消?";
"已收藏,点击取消收藏" = "已收藏,點擊取消收藏";
"收藏当前选点" = "收藏當前選點";
"搜索或点击地图选点后,保存为收藏。" = "搜索或點擊地圖選點後,保存為收藏。";
"切换到此处" = "切換到此處";
"同步到第三方代理" = "同步到第三方代理";
"检测并同步中…" = "檢測並同步中…";
"停止第三方虚拟定位" = "停止第三方虛擬定位";
"开始虚拟定位" = "開始虛擬定位";
"验证环境中…" = "驗證環境中…";
"停止虚拟定位" = "停止虛擬定位";
"当前未连接可用的 Wi-Fi,请连接 Wi-Fi 后配置 127.0.0.1:8888 手动代理。" = "當前未連接可用的 Wi-Fi,請連接 Wi-Fi 後配置 127.0.0.1:8888 手動代理。";
"第三方代理查询失败" = "第三方代理查詢失敗";
"未命名" = "未命名";
"没有找到相关地点" = "沒有找到相關地點";
"虚拟定位已开启" = "虛擬定位已開啓";
"不再提醒" = "不再提醒";
"虚拟定位已关闭" = "虛擬定位已關閉";
"选择模式" = "選擇模式";
"配置 Wi-Fi 代理" = "配置 Wi-Fi 代理";
"初始化 CA 证书" = "初始化 CA 證書";
"选择客户端" = "選擇客戶端";
"导入并检测" = "導入並檢測";
"上一步" = "上一步";
"开始使用" = "開始使用";
"操作失败" = "操作失敗";
"选择运行模式" = "選擇運行模式";
"后续可在“设置 → 运行模式”中切换。两种模式不要同时拦截 WLOC 请求。" = "後續可在“設置 → 運行模式”中切換。兩種模式不要同時攔截 WLOC 請求。";
"仅 Wi-Fi" = "僅 Wi-Fi";
"无外部依赖" = "無外部依賴";
"App 在设备本地启动代理,通过当前 Wi-Fi 的手动 HTTP 代理改写定位响应。免费自签应用无法使用系统 VPN 的 Network Extension 能力,因此 APP模式不支持蜂窝网络,需要配置 Wi-Fi 代理并安装 App 生成的 CA。" = "App 在設備本地啓動代理,通過當前 Wi-Fi 的手動 HTTP 代理改寫定位響應。免費自簽應用無法使用系統 VPN 的 Network Extension 能力,因此 APP模式不支持蜂窩網絡,需要配置 Wi-Fi 代理並安裝 App 生成的 CA。";
"测试模式" = "測試模式";
"App 负责选点,并通过 WLOC 配置接口查询和同步坐标;第三方代理客户端负责网络代理、模块拦截、MITM 和持久化。证书、VPN 与代理连接均由第三方客户端处理。" = "App 負責選點,並通過 WLOC 配置接口查詢和同步坐標;第三方代理客戶端負責網絡代理、模塊攔截、MITM 和持久化。證書、VPN 與代理連接均由第三方客戶端處理。";
"正在准备 APP模式本地服务…" = "正在準備 APP模式本地服務…";
"先配置 Wi-Fi 系统代理" = "先配置 Wi-Fi 系統代理";
"在当前 Wi-Fi 的详情页,将「HTTP 代理」设为「手动」:服务器填 127.0.0.1,端口填 8888。配置后点击下方「完成」。检测会自动判断是 Wi-Fi 代理还是证书信任有问题。" = "在當前 Wi-Fi 的詳情頁,將「HTTP 代理」設為「手動」:服務器填 127.0.0.1,端口填 8888。配置後點擊下方「完成」。檢測會自動判斷是 Wi-Fi 代理還是證書信任有問題。";
"Wi-Fi 代理设置" = "Wi-Fi 代理設置";
"1 选择手动,2 填写服务器 127.0.0.1,3 填写端口 8888。" = "1 選擇手動,2 填寫服務器 127.0.0.1,3 填寫端口 8888。";
"复制地址" = "複製地址";
"打开 Wi-Fi 设置" = "打開 Wi-Fi 設置";
"第 1 步:下载证书" = "第 1 步:下載證書";
"下载本机随机生成的 CA 根证书。私钥仅保存在此设备的钥匙串中,不会随证书文件导出。App 会弹出 Safari 下载页;出现配置描述文件下载提示时,选择「允许」。" = "下載本機隨機生成的 CA 根證書。私鑰僅保存在此設備的鑰匙串中,不會隨證書文件導出。App 會彈出 Safari 下載頁;出現配置描述文件下載提示時,選擇「允許」。";
"打开下载页" = "打開下載頁";
"无法准备证书下载页面,请查看诊断日志" = "無法準備證書下載頁面,請查看診斷日誌";
"第 2 步:安装证书" = "第 2 步:安裝證書";
"下载完成后打开系统「设置」。如果顶部显示「已下载描述文件」,点进去安装;否则进入「通用 → VPN 与设备管理」,找到 Location Spoofer CA 并完成安装。" = "下載完成後打開系統「設置」。如果頂部顯示「已下載描述文件」,點進去安裝;否則進入「通用 → VPN 與設備管理」,找到 Location Spoofer CA 並完成安裝。";
"去安装" = "去安裝";
"安装证书" = "安裝證書";
"1 在「VPN 与设备管理」中打开 Location Spoofer CA 描述文件并完成安装。" = "1 在「VPN 與設備管理」中打開 Location Spoofer CA 描述文件並完成安裝。";
"第 3 步:信任证书" = "第 3 步:信任證書";
"安装后进入「设置 → 通用 → 关于本机 → 证书信任设置」,找到 Location Spoofer CA 并开启完全信任。iOS 保留钥匙串数据时,重装 App 会继续复用同一证书。" = "安裝後進入「設置 → 通用 → 關於本機 → 證書信任設置」,找到 Location Spoofer CA 並開啓完全信任。iOS 保留鑰匙串數據時,重裝 App 會繼續復用同一證書。";
"去信任" = "去信任";
"信任证书" = "信任證書";
"1 在「证书信任设置」中为 Location Spoofer CA 开启完全信任。" = "1 在「證書信任設置」中為 Location Spoofer CA 開啓完全信任。";
"已完成" = "已完成";
"已完成 ✓" = "已完成 ✓";
"iOS 27 beta 6 起,系统已禁止对 gs-loc.apple.com 进行 MITM 拦截。该版本及之后的 beta 版本暂时无法使用本项目,等待后续适配方案。" = "iOS 27 beta 6 起,系統已禁止對 gs-loc.apple.com 進行 MITM 攔截。該版本及之後的 beta 版本暫時無法使用本項目,等待後續適配方案。";
"选择第三方代理客户端" = "選擇第三方代理客戶端";
"除 Shadowrocket 外,当前客户端配置尚未完成真机验证,页面只提供模块导入入口和通用配置提醒。" = "除 Shadowrocket 外,當前客戶端配置尚未完成真機驗證,頁面只提供模塊導入入口和通用配置提醒。";
"第三方客户端适配说明" = "第三方客戶端適配說明";
"App 不连接远程坐标服务器,而是向 Apple 域名发起一个约定请求。第三方客户端需要在本机拦截该请求、保存 WGS-84 坐标并返回 JSON;定位模块再读取同一份数据,修改 Apple WLOC 响应。" = "App 不連接遠程坐標服務器,而是向 Apple 域名發起一個約定請求。第三方客戶端需要在本機攔截該請求、保存 WGS-84 坐標並返回 JSON;定位模塊再讀取同一份數據,修改 Apple WLOC 響應。";
"配置接口" = "配置接口";
"查询:GET ?action=query\n保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>\n清除:GET ?action=clear" = "查詢:GET ?action=query\n保存:GET ?lon=<經度>&lat=<緯度>&acc=<精度>\n清除:GET ?action=clear";
"返回格式" = "返回格式";
"适配要求" = "適配要求";
"客户端需要支持请求脚本、持久化存储、HTTP 200 JSON 响应、Apple WLOC 响应脚本,以及 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的 HTTPS 解密。保存接口和 WLOC 响应脚本必须读取同一份持久化数据。" = "客戶端需要支持請求腳本、持久化存儲、HTTP 200 JSON 響應、Apple WLOC 響應腳本,以及 Apple 定位域名(gs-loc.apple.com、gsp-ssl.ls.apple.com、bluedot.is.autonavi.com 等)的 HTTPS 解密。保存接口和 WLOC 響應腳本必須讀取同一份持久化數據。";
"检测到第三方代理连接异常,请检查模块、MITM 和代理连接后重新检测。" = "檢測到第三方代理連接異常,請檢查模塊、MITM 和代理連接後重新檢測。";
"第 1 步:导入 %@ 模块" = "第 1 步:導入 %@ 模塊";
"复制 %@ 的模块订阅地址。" = "複製 %@ 的模塊訂閱地址。";
"打开 Shadowrocket,进入“配置 → 模块”。" = "打開 Shadowrocket,進入“配置 → 模塊”。";
"打开 %@。" = "打開 %@。";
"进入 Shadowrocket 配置" = "進入 Shadowrocket 配置";
"1 点击「模块」进入模块列表,2 可打开当前本地配置详情。" = "1 點擊「模塊」進入模塊列表,2 可打開當前本地配置詳情。";
"点击右上角“+”,粘贴模块订阅地址并导入,然后确认模块已启用。" = "點擊右上角“+”,粘貼模塊訂閱地址並導入,然後確認模塊已啓用。";
"导入 Shadowrocket 模块" = "導入 Shadowrocket 模塊";
"1 点击右上角加号导入模块,2 确认模块已启用。" = "1 點擊右上角加號導入模塊,2 確認模塊已啓用。";
"在 %@ 中导入刚才复制的模块订阅地址。" = "在 %@ 中導入剛才複製的模塊訂閱地址。";
"第 2 步:完成 %@ 配置" = "第 2 步:完成 %@ 配置";
"请在 %@ 中完成相应配置。" = "請在 %@ 中完成相應配置。";
"配置时请复制下方全部解密域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)。" = "配置時請複製下方全部解密域名(含 gsp-ssl.ls.apple.com、bluedot.is.autonavi.com)。";
"第 2 步:配置 HTTPS 解密" = "第 2 步:配置 HTTPS 解密";
"进入“配置 → 本地文件”,找到带黄点的配置,点击右侧 i 图标。" = "進入“配置 → 本地文件”,找到帶黃點的配置,點擊右側 i 圖標。";
"进入“HTTPS 解密”,开启解密开关。" = "進入“HTTPS 解密”,開啓解密開關。";
"在域名列表中添加下方复制的全部解密域名。" = "在域名列表中添加下方複製的全部解密域名。";
"配置 HTTPS 解密" = "配置 HTTPS 解密";
"1 开启 HTTPS 解密,2 添加复制的全部解密域名,3 打开证书设置。" = "1 開啓 HTTPS 解密,2 添加複製的全部解密域名,3 打開證書設置。";
"按 Shadowrocket 提示生成并完成证书授权。" = "按 Shadowrocket 提示生成並完成證書授權。";
"授权 Shadowrocket 证书" = "授權 Shadowrocket 證書";
"1 打开 Shadowrocket 证书项并按提示安装、授权。" = "1 打開 Shadowrocket 證書項並按提示安裝、授權。";
"返回 HTTPS 解密页面,点击右上角勾号保存,然后开启代理。" = "返回 HTTPS 解密頁面,點擊右上角勾號保存,然後開啓代理。";
"打开 Shadowrocket 继续配置" = "打開 Shadowrocket 繼續配置";
"App 只能唤起 Shadowrocket,无法通过公开接口直接跳转到“模块”或“HTTPS 解密”页面。" = "App 只能喚起 Shadowrocket,無法通過公開接口直接跳轉到“模塊”或“HTTPS 解密”頁面。";
"轻点查看大图" = "輕點查看大圖";
"证书尚未安装或信任" = "證書尚未安裝或信任";
"Wi-Fi 代理未正确设置" = "Wi-Fi 代理未正確設置";
"本地代理未能启动" = "本地代理未能啓動";
"检测仍在进行" = "檢測仍在進行";
"检测结果已过期" = "檢測結果已過期";
"定位改写检测失败" = "定位改寫檢測失敗";
"接口连接失败" = "接口連接失敗";
"本地代理初始化失败:%@" = "本地代理初始化失敗:%@";
"✓ 定位环境正常" = "✓ 定位環境正常";
"CA 证书未安装或未信任" = "CA 證書未安裝或未信任";
"Wi-Fi 代理未正确设置,请检查 127.0.0.1:8888" = "Wi-Fi 代理未正確設置,請檢查 127.0.0.1:8888";
"请手动打开「设置」,找到本 App 后检查定位权限。" = "請手動打開「設置」,找到本 App 後檢查定位權限。";
"请手动打开「设置 → 通用」。" = "請手動打開「設置 → 通用」。";
"请手动打开「设置 → 无线局域网」,进入当前 Wi-Fi 的详情页。" = "請手動打開「設置 → 無線局域網」,進入當前 Wi-Fi 的詳情頁。";
"请手动打开「设置 → 隐私与安全性 → 定位服务」。" = "請手動打開「設置 → 隱私與安全性 → 定位服務」。";
"第三方代理返回了无法识别的数据" = "第三方代理返回了無法識別的數據";
"请求未被第三方代理模块拦截,请检查模块、MITM 和代理连接" = "請求未被第三方代理模塊攔截,請檢查模塊、MITM 和代理連接";
"第三方代理保存的坐标与当前选点不一致" = "第三方代理保存的坐標與當前選點不一致";
"第三方代理请求失败:%@" = "第三方代理請求失敗:%@";
"检查模块、MITM、证书和代理/VPN连接" = "檢查模塊、MITM、證書和代理/VPN連接";
"第三方代理拒绝保存坐标" = "第三方代理拒絕保存坐標";
"第三方代理清除坐标失败" = "第三方代理清除坐標失敗";
"已有第三方代理请求正在执行" = "已有第三方代理請求正在執行";
"配置已提供,尚未验证" = "配置已提供,尚未驗證";
"已连接,有保存坐标" = "已連接,有保存坐標";
"已连接,无保存坐标" = "已連接,無保存坐標";
"连接失败(%@)" = "連接失敗(%@)";
"当前客户端:%@" = "當前客戶端:%@";
"配置接口:/wloc-settings/save" = "配置接口:/wloc-settings/save";
"请求动作:WLOC query" = "請求動作:WLOC query";
"检查范围:模块拦截、MITM、证书、代理/VPN 连接" = "檢查範圍:模塊攔截、MITM、證書、代理/VPN 連接";
"连接状态:%@" = "連接狀態:%@";
"检测结果:失败" = "檢測結果:失敗";
"耗时:%lld ms" = "耗時:%lld ms";
"错误类型:%@" = "錯誤類型:%@";
"错误详情:%@" = "錯誤詳情:%@";
"处理建议:%@。" = "處理建議:%@。";
"======== 第三方代理运行检测 ========" = "======== 第三方代理運行檢測 ========";
"触发来源:地图或设置中的第三方代理操作" = "觸發來源:地圖或設置中的第三方代理操作";
"请求动作:WLOC 配置接口" = "請求動作:WLOC 配置接口";
"处理建议:确认模块已启用,并检查 MITM、证书和代理/VPN 连接。" = "處理建議:確認模塊已啓用,並檢查 MITM、證書和代理/VPN 連接。";
"环境信息" = "環境信息";
"App 版本" = "App 版本";
"系统版本" = "系統版本";
"诊断日志" = "診斷日誌";
"不适用" = "不適用";
"是" = "是";
"否" = "否";
"(无诊断数据)" = "(無診斷數據)";
"第三方代理测试模式:模块连接成功;已保存坐标=%@" = "第三方代理測試模式:模塊連接成功;已保存坐標=%@";
"第三方代理测试模式:模块连接失败;%@" = "第三方代理測試模式:模塊連接失敗;%@";
"======== 代理验证测试 ========" = "======== 代理驗證測試 ========";
"App 版本: %@" = "App 版本: %@";
"系统版本: iOS %@" = "系統版本: iOS %@";
"[步骤 A] 检查代理是否运行…" = "[步驟 A] 檢查代理是否運行…";
" 端口: 127.0.0.1:8888" = " 端口: 127.0.0.1:8888";
" ⚠ 代理未运行,尝试启动…" = " ⚠ 代理未運行,嘗試啓動…";
" ✗ 启动失败: %@" = " ✗ 啓動失敗: %@";
" ✓ 代理启动成功" = " ✓ 代理啓動成功";
" ✓ 代理已在运行中" = " ✓ 代理已在運行中";
"[步骤 B] 检测证书与 WiFi 代理…" = "[步驟 B] 檢測證書與 WiFi 代理…";
" 方式: 请求 baidu.com/paopao-verify-<token>" = " 方式: 請求 baidu.com/paopao-verify-<token>";
" 结果判定: TLS 错误=证书问题 / 响应不匹配=代理未配置 / 匹配=通过" = " 結果判定: TLS 錯誤=證書問題 / 響應不匹配=代理未配置 / 匹配=通過";
" ✗ 无法生成验证 token" = " ✗ 無法生成驗證 token";
" ✓ 证书已信任,WiFi 代理已配置" = " ✓ 證書已信任,WiFi 代理已配置";
" ✗ 响应不匹配: HTTP %lld, %lld bytes,WiFi 代理未配置" = " ✗ 響應不匹配: HTTP %lld, %lld bytes,WiFi 代理未配置";
" ✗ 请求失败 [%@ code=%lld]: %@" = " ✗ 請求失敗 [%@ code=%lld]: %@";
" TLS/证书校验失败,CA 证书未信任" = " TLS/證書校驗失敗,CA 證書未信任";
"======== 环境检测通过 ✓ ========" = "======== 環境檢測通過 ✓ ========";
" --- 代理日志 ---" = " --- 代理日誌 ---";
"成功:{\"success\":true,\"longitude\":113.0,\"latitude\":22.0,\"accuracy\":25}\n失败:{\"success\":false,\"error\":\"错误说明\"}" = "成功:{\"success\":true,\"longitude\":113.0,\"latitude\":22.0,\"accuracy\":25}\n失敗:{\"success\":false,\"error\":\"錯誤說明\"}";
"定位改写脚本源自 Yu9191/wloc,现由本项目维护" = "定位改寫腳本源自 Yu9191/wloc,現由本項目維護";
"地图" = "地圖";
"坐标转换" = "坐標轉換";
"定位" = "定位";
"实时定位" = "實時定位";
"提醒" = "提醒";
"搜索" = "搜索";
"收藏" = "收藏";
"缩放" = "縮放";
"========== App 启动 ==========" = "========== App 啓動 ==========";
"尚未选择运行模式,跳过本地 CA 和代理初始化" = "尚未選擇運行模式,跳過本地 CA 和代理初始化";
"第三方代理测试模式:跳过本地 CA、代理和环境检测" = "第三方代理測試模式:跳過本地 CA、代理和環境檢測";
"旧坐标数据迁移失败,将在下次启动重试" = "舊坐標數據遷移失敗,將在下次啓動重試";
"地图坐标标准初始化完成,开始后续启动流程" = "地圖坐標標準初始化完成,開始後續啓動流程";
"没有持久化图钉,地图创建前请求实时定位" = "沒有持久化圖釘,地圖創建前請求實時定位";
"已使用实时定位准备唯一初始地图状态" = "已使用實時定位準備唯一初始地圖狀態";
"地图创建前取得的初始实时位置(WGS-84)" = "地圖創建前取得的初始實時位置(WGS-84)";
"地图创建前无法取得实时定位,唯一初始位置使用深圳" = "地圖創建前無法取得實時定位,唯一初始位置使用深圳";
"已找到持久化图钉,直接准备唯一初始地图状态" = "已找到持久化圖釘,直接準備唯一初始地圖狀態";
"启动门禁全部完成,现在创建 MapHomeView" = "啓動門禁全部完成,現在創建 MapHomeView";
"初始化" = "初始化";
"启动代理 127.0.0.1:8888" = "啓動代理 127.0.0.1:8888";
"启动代理: 恢复上次 WGS-84 定位" = "啓動代理: 恢復上次 WGS-84 定位";
"启动成功" = "啓動成功";
"启动失败" = "啓動失敗";
"写入坐标" = "寫入坐標";
"跳过过期坐标写入" = "跳過過期坐標寫入";
"跳过旧验证坐标恢复" = "跳過舊驗證坐標恢復";
"运动状态模拟设置已更新" = "運動狀態模擬設置已更新";
"准备证书下载失败" = "準備證書下載失敗";
"设置虚拟定位坐标" = "設置虛擬定位坐標";
"apply失败" = "apply失敗";
"保存收藏失败" = "保存收藏失敗";
"本地服务初始化失败" = "本地服務初始化失敗";
"代理运行模式已切换" = "代理運行模式已切換";
"已迁移旧版模式初始化状态" = "已遷移舊版模式初始化狀態";
"地图坐标标准检测已有请求进行中" = "地圖坐標標準檢測已有請求進行中";
"地图坐标标准检测开始" = "地圖坐標標準檢測開始";
"地图坐标标准检测获得明确结果" = "地圖坐標標準檢測獲得明確結果";
"地图坐标标准检测不可用,开始实时定位兜底" = "地圖坐標標準檢測不可用,開始實時定位兜底";
"地图坐标标准检测使用兜底结果" = "地圖坐標標準檢測使用兜底結果";
"地图坐标标准检测被取消,保留默认国内标准" = "地圖坐標標準檢測被取消,保留默認國內標準";
"地图坐标标准已确定,允许创建地图" = "地圖坐標標準已確定,允許創建地圖";
"地图坐标标准运行期检测合并到进行中请求" = "地圖坐標標準運行期檢測合併到進行中請求";
"地图坐标标准运行期检测开始" = "地圖坐標標準運行期檢測開始";
"地图坐标标准运行期检测结果已过期,取消写入" = "地圖坐標標準運行期檢測結果已過期,取消寫入";
"地图坐标标准运行期检测完成,标准未变化" = "地圖坐標標準運行期檢測完成,標準未變化";
"地图坐标标准运行期检测发现切换" = "地圖坐標標準運行期檢測發現切換";
"地图坐标标准运行期检测失败,保留当前标准" = "地圖坐標標準運行期檢測失敗,保留當前標準";
"地图坐标标准运行期检测已取消" = "地圖坐標標準運行期檢測已取消";
"实时定位不覆盖固定锚点的明确检测结果" = "實時定位不覆蓋固定錨點的明確檢測結果";
"实时定位确认兜底地图坐标标准无需修正" = "實時定位確認兜底地圖坐標標準無需修正";
"实时定位修正启动兜底地图坐标标准" = "實時定位修正啓動兜底地圖坐標標準";
"第三方代理已保存 WGS-84 坐标" = "第三方代理已保存 WGS-84 坐標";
"第三方代理坐标已清除" = "第三方代理坐標已清除";
"第三方代理请求失败" = "第三方代理請求失敗";
"第三方代理坐标同步成功" = "第三方代理坐標同步成功";
"同步坐标到第三方客户端失败" = "同步坐標到第三方客戶端失敗";
"验证结果" = "驗證結果";
"验证失败" = "驗證失敗";
"开启前检测失败,进入对应环境引导" = "開啓前檢測失敗,進入對應環境引導";
"清除第三方客户端坐标失败" = "清除第三方客戶端坐標失敗";
"累计虚拟定位操作次数" = "累計虛擬定位操作次數";
"已复制坐标" = "已複製坐標";
"地图坐标标准切换时未找到当前选点缓存" = "地圖坐標標準切換時未找到當前選點緩存";
"地图坐标标准切换后已使用缓存坐标对回显当前选点" = "地圖坐標標準切換後已使用緩存坐標對回顯當前選點";
"地图坐标类型已变化" = "地圖坐標類型已變化";
"取消保存收藏:检测期间当前选点已变化" = "取消保存收藏:檢測期間當前選點已變化";
"保存当前选点为收藏" = "保存當前選點為收藏";
"第三方代理查询返回失败" = "第三方代理查詢返回失敗";
"启动后第三方代理状态查询失败" = "啓動後第三方代理狀態查詢失敗";
"检测到 Wi-Fi 网络变化" = "檢測到 Wi-Fi 網絡變化";
"网络仍在变化,重新计算环境检测等待时间" = "網絡仍在變化,重新計算環境檢測等待時間";
"等待 Wi-Fi 连接稳定后检测" = "等待 Wi-Fi 連接穩定後檢測";
"延时检测已被更新的网络事件取消" = "延時檢測已被更新的網絡事件取消";
"稳定等待结束后仍未连接 Wi-Fi,提示检查代理" = "穩定等待結束後仍未連接 Wi-Fi,提示檢查代理";
"开始后台环境检测" = "開始後台環境檢測";
"已有环境检测运行,1 秒后重试" = "已有環境檢測運行,1 秒後重試";
"后台环境检测完成" = "後台環境檢測完成";
"环境检测连续被占用,本次不重复弹窗" = "環境檢測連續被佔用,本次不重復彈窗";
"用户点击实时定位" = "用戶點擊實時定位";
"MapKit 蓝点尚不可用,启动 CLLocationManager 兜底" = "MapKit 藍點尚不可用,啓動 CLLocationManager 兜底";
"MapKit 蓝点抢先完成请求,取消 CLLocationManager 兜底" = "MapKit 藍點搶先完成請求,取消 CLLocationManager 兜底";
"虚拟定位开启后的 MapKit 蓝点标准判定" = "虛擬定位開啓後的 MapKit 藍點標準判定";
"登记实时定位请求上下文" = "登記實時定位請求上下文";
"复用进行中的 CLLocationManager 请求并更新意图上下文" = "復用進行中的 CLLocationManager 請求並更新意圖上下文";
"CLLocationManager 兜底未返回坐标" = "CLLocationManager 兜底未返回坐標";
"丢弃 CLLocationManager 结果:任务已取消或蓝点已抢先完成" = "丟棄 CLLocationManager 結果:任務已取消或藍點已搶先完成";
"实时定位坐标完成标准判断并提交到地图" = "實時定位坐標完成標準判斷並提交到地圖";
"MKLocalSearch 返回地点结果" = "MKLocalSearch 返回地點結果";
"反向地理编码网络失败,准备重试" = "反向地理編碼網絡失敗,準備重試";
"反向地理编码失败" = "反向地理編碼失敗";
"获得搜索结果" = "獲得搜索結果";
"点击收藏点并回显到地图" = "點擊收藏點並回顯到地圖";
"忽略重复 CLLocationManager 请求" = "忽略重復 CLLocationManager 請求";
"CLLocationManager 请求入口" = "CLLocationManager 請求入口";
"授权状态不允许定位" = "授權狀態不允許定位";
"创建 CLLocationManager 请求" = "創建 CLLocationManager 請求";
"请求前台定位授权" = "請求前台定位授權";
"定位授权状态变化" = "定位授權狀態變化";
"CLLocationManager 返回样本批次" = "CLLocationManager 返回樣本批次";
"本批次没有可完成当前请求的样本" = "本批次沒有可完成當前請求的樣本";
"接受 CLLocationManager 样本并完成请求" = "接受 CLLocationManager 樣本並完成請求";
"定位回调失败" = "定位回調失敗";
"跳过 CLLocationManager 缓存样本" = "跳過 CLLocationManager 緩存樣本";
"等待定位授权超时" = "等待定位授權超時";
"单次定位超时,切换持续定位" = "單次定位超時,切換持續定位";
"持续定位超时" = "持續定位超時";
"开始 CLLocationManager 单次定位" = "開始 CLLocationManager 單次定位";
"开始 CLLocationManager 持续定位兜底" = "開始 CLLocationManager 持續定位兜底";
"CLLocationManager 请求完成" = "CLLocationManager 請求完成";
"CLLocationManager 请求结束但没有坐标" = "CLLocationManager 請求結束但沒有坐標";
"使用 CLLocationManager 新鲜缓存" = "使用 CLLocationManager 新鮮緩存";
"实时定位按钮直接使用 MapKit 蓝点缓存" = "實時定位按鈕直接使用 MapKit 藍點緩存";
"主页收到待处理请求所需的 MapKit 蓝点回调" = "主頁收到待處理請求所需的 MapKit 藍點回調";
"主页收到 CLLocationManager 兜底坐标" = "主頁收到 CLLocationManager 兜底坐標";
"原始实时定位坐标" = "原始實時定位坐標";
"检查 CLLocationManager 样本" = "檢查 CLLocationManager 樣本";
"拒绝 CLLocationManager 样本:坐标或精度无效" = "拒絕 CLLocationManager 樣本:坐標或精度無效";
"MapKit 蓝点更新但 location 为空" = "MapKit 藍點更新但 location 為空";
"拒绝 MapKit 蓝点样本:坐标无效" = "拒絕 MapKit 藍點樣本:坐標無效";
"拒绝 MapKit 蓝点样本:水平精度无效" = "拒絕 MapKit 藍點樣本:水平精度無效";
"音频中断恢复" = "音頻中斷恢復";
"音频会话失败" = "音頻會話失敗";
"无法创建静音音频缓冲区" = "無法創建靜音音頻緩衝區";
"引擎启动失败" = "引擎啓動失敗";
"后台保活已启动(静音音频)" = "後台保活已啓動(靜音音頻)";
"后台保活已停止" = "後台保活已停止";
"保存当前图钉失败" = "保存當前圖釘失敗";
"存储缩放" = "存儲縮放";
"旧坐标数据迁移完成" = "舊坐標數據遷移完成";
"远程版本配置加载成功" = "遠程版本配置加載成功";
"版本配置源不可用,尝试下一地址" = "版本配置源不可用,嘗試下一地址";
"版本配置加载失败,继续使用内置配置" = "版本配置加載失敗,繼續使用內置配置";
"版本说明源不可用,尝试下一地址" = "版本說明源不可用,嘗試下一地址";
"版本说明加载失败,将使用最新 Release 页面" = "版本說明加載失敗,將使用最新 Release 頁面";
"复用设备钥匙串中的 CA" = "復用設備鑰匙串中的 CA";
"旧 CA 已迁移到设备钥匙串" = "舊 CA 已遷移到設備鑰匙串";
"已生成并保存设备专属 CA" = "已生成並保存設備專屬 CA";
"已删除设备 CA,等待重新生成" = "已刪除設備 CA,等待重新生成";
"钥匙串中的 CA 无效,准备回退" = "鑰匙串中的 CA 無效,準備回退";
"删除旧 CA 文件失败,将在下次启动重试" = "刪除舊 CA 文件失敗,將在下次啓動重試";
"无法解析 CA 证书 PEM" = "無法解析 CA 證書 PEM";
"无法创建 SecTrust" = "無法創建 SecTrust";
"SecTrust 评估返回错误" = "SecTrust 評估返回錯誤";
"CA 证书已被系统信任" = "CA 證書已被系統信任";
"CA 证书未被系统信任" = "CA 證書未被系統信任";
"调用 Go Core 生成 CA" = "調用 Go Core 生成 CA";
"Go Core CA 生成成功" = "Go Core CA 生成成功";
"本地证书服务已在运行" = "本地證書服務已在運行";
"调用 Go Core 启动本地证书服务" = "調用 Go Core 啓動本地證書服務";
"本地证书服务启动成功" = "本地證書服務啓動成功";
"停止本地证书服务" = "停止本地證書服務";
"开始第三方代理连接检测" = "開始第三方代理連接檢測";
"第三方代理连接检测通过" = "第三方代理連接檢測通過";
"第三方代理连接检测失败" = "第三方代理連接檢測失敗";
"关闭代理前已同步关闭虚拟定位" = "關閉代理前已同步關閉虛擬定位";
"切换 APP 模式前无法清除第三方坐标" = "切換 APP 模式前無法清除第三方坐標";
"设置页第三方连接检测通过" = "設置頁第三方連接檢測通過";
"设置页第三方连接检测失败" = "設置頁第三方連接檢測失敗";
"地图实际显示坐标" = "地圖實際顯示坐標";
"App回到前台" = "App回到前台";
"App已确认地图标准" = "App已確認地圖標準";
"CLLocationManager请求中" = "CLLocationManager請求中";
"Go proxy 启动失败" = "Go proxy 啓動失敗";
"MapKit地图蓝点" = "MapKit地圖藍點";
"MapKit蓝点新样本" = "MapKit藍點新樣本";
"MapKit蓝点缓存" = "MapKit藍點緩存";
"SSID可读取" = "SSID可讀取";
"WLOC写入标准" = "WLOC寫入標準";
"WLOC目标标准" = "WLOC目標標準";
"Wi-Fi接口" = "Wi-Fi接口";
"intent选点revision" = "intent選點revision";
"manager请求中" = "manager請求中";
"上下文存在" = "上下文存在";
"事件原因" = "事件原因";
"任务已取消" = "任務已取消";
"任务已存在" = "任務已存在";
"使用兜底" = "使用兜底";
"保存收藏" = "保存收藏";
"保存数据包含" = "保存數據包含";
"保持未启用" = "保持未啓用";
"保留原第三方坐标" = "保留原第三方坐標";
"保留已启用状态" = "保留已啓用狀態";
"保留标准" = "保留標準";
"修正兜底标准" = "修正兜底標準";
"内存缓存存在" = "內存緩存存在";
"初始坐标来源" = "初始坐標來源";
"初始来源" = "初始來源";
"初始阶段" = "初始階段";
"判定规则" = "判定規則";
"原兜底来源" = "原兜底來源";
"原因" = "原因";
"取值字段" = "取值字段";
"可显示不再提醒" = "可顯示不再提醒";
"启动代理…" = "啓動代理…";
"命中林士街" = "命中林士街";
"固定锚点" = "固定錨點";
"固定锚点查询超过5秒" = "固定錨點查詢超過5秒";
"固定锚点查询返回空结果" = "固定錨點查詢返回空結果";
"国内标准(GCJ-02)" = "國內標準(GCJ-02)";
"国内转换区域" = "國內轉換區域";
"国外非转换区域" = "國外非轉換區域";
"国际标准(WGS-84)" = "國際標準(WGS-84)";
"国际标准(WGS-84)+国内标准(GCJ-02)" = "國際標準(WGS-84)+國內標準(GCJ-02)";
"图钉已按新类型重设" = "圖釘已按新類型重設";
"地图取值字段" = "地圖取值字段";
"地图标准" = "地圖標準";
"坐标有效" = "坐標有效";
"坐标标准" = "坐標標準";
"垂直精度米" = "垂直精度米";
"基础校验通过" = "基礎校驗通過";
"处理建议" = "處理建議";
"失败时提示" = "失敗時提示";
"实时定位存在" = "實時定位存在";
"实时定位服务区域" = "實時定位服務區域";
"客户端模式" = "客戶端模式";
"尝试" = "嘗試";
"已取消位置更新" = "已取消位置更新";
"已恢复真实定位" = "已恢復真實定位";
"已清理" = "已清理";
"已重置" = "已重置";
"开启" = "開啓";
"异步地理编码" = "異步地理編碼";
"当前地图标准" = "當前地圖標準";
"当前客户端" = "當前客戶端";
"当前标准" = "當前標準";
"当前选点revision" = "當前選點revision";
"恢复状态" = "恢復狀態";
"批次索引" = "批次索引";
"持久化字段" = "持久化字段";
"授权状态" = "授權狀態";
"授权状态rawValue" = "授權狀態rawValue";
"探测失败原因" = "探測失敗原因";
"搜索结果" = "搜索結果";
"操作" = "操作";
"新类型" = "新類型";
"无已保存" = "無已保存";
"无法判定" = "無法判定";
"无法启动本地证书服务" = "無法啓動本地證書服務";
"无法生成本地证书" = "無法生成本地證書";
"日志策略" = "日誌策略";
"旧类型" = "舊類型";
"显示坐标字段" = "顯示坐標字段";
"最低版本" = "最低版本";
"最新版本" = "最新版本";
"最终标准" = "最終標準";
"最终阶段" = "最終階段";
"有坐标" = "有坐標";
"有效样本数" = "有效樣本數";
"有缓存" = "有緩存";
"未知错误" = "未知錯誤";
"本地 CA 证书或私钥无效" = "本地 CA 證書或私鑰無效";
"无法写入设备钥匙串(%d)" = "無法寫入設備鑰匙串(%d)";
"来源" = "來源";
"来源类型" = "來源類型";
"林士街" = "林士街";
"查看诊断日志" = "查看診斷日誌";
"样本年龄秒" = "樣本年齡秒";
"样本数" = "樣本數";
"样本时间" = "樣本時間";
"检查范围" = "檢查範圍";
"检测前标准" = "檢測前標準";
"模块拦截、MITM、代理/VPN连接" = "模塊攔截、MITM、代理/VPN連接";
"次数" = "次數";
"每次开启首次或判定变化" = "每次開啓首次或判定變化";
"水平精度米" = "水平精度米";
"活动requestID" = "活動requestID";
"活动阶段" = "活動階段";
"海拔米" = "海拔米";
"满足当前请求时间窗" = "滿足當前請求時間窗";
"点击实时定位" = "點擊實時定位";
"目标所在区域" = "目標所在區域";
"确认标准" = "確認標準";
"社区征集客户端数" = "社區徵集客戶端數";
"等待秒数" = "等待秒數";
"系统缓存存在" = "系統緩存存在";
"结果" = "結果";
"结果数" = "結果數";
"结果来源" = "結果來源";
"缓存" = "緩存";
"缓存上限秒" = "緩存上限秒";
"缓存时效通过" = "緩存時效通過";
"缩放米" = "縮放米";
"网络可用" = "網絡可用";
"耗时毫秒" = "耗時毫秒";
"蓝点回调更接近" = "藍點回調更接近";
"蓝点缓存" = "藍點緩存";
"蓝点缓存存在" = "藍點緩存存在";
"蓝点距GCJ目标米" = "藍點距GCJ目標米";
"蓝点距WGS目标米" = "藍點距WGS目標米";
"触发原因" = "觸發原因";
"诊断位置" = "診斷位置";
"请求动作" = "請求動作";
"超时毫秒" = "超時毫秒";
"输入坐标标准" = "輸入坐標標準";
"连接状态" = "連接狀態";
"选点revision" = "選點revision";
"选点期间发生变化" = "選點期間發生變化";
"错误" = "錯誤";
"错误类型" = "錯誤類型";
"锚点" = "錨點";
"阶段" = "階段";
"首条名称" = "首條名稱";
"首条名称=林士街→GCJ-02,否则→WGS-84" = "首條名稱=林士街→GCJ-02,否則→WGS-84";
"默认国内标准" = "默認國內標準";
"证书下载地址无效" = "證書下載地址無效";
"启动代理失败: %@" = "啓動代理失敗: %@";
@@ -0,0 +1,34 @@
import Foundation
// Chinese source keys are extracted from the maintained English table so all
// shipped languages resolve the same keys. Keep both Chinese tables explicit:
// an absent table falls back to the development language (English), not the key.
let root = URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent()
let resources = root.appendingPathComponent("Resources")
let english = try String(
contentsOf: resources.appendingPathComponent("en.lproj/Localizable.strings"),
encoding: .utf8
)
let pattern = try NSRegularExpression(
pattern: #"^"((?:\\.|[^"\\])*)"\s*=\s*"(?:\\.|[^"\\])*";"#,
options: .anchorsMatchLines
)
let range = NSRange(english.startIndex..<english.endIndex, in: english)
let keys = pattern.matches(in: english, range: range).compactMap { match -> String? in
guard let keyRange = Range(match.range(at: 1), in: english) else { return nil }
return String(english[keyRange])
}
precondition(!keys.isEmpty && Set(keys).count == keys.count, "English keys must be nonempty and unique")
for (locale, transform) in [("zh-Hans", false), ("zh-Hant", true)] {
var lines = ["/* Generated from en.lproj/Localizable.strings by Scripts/generate-chinese-localizations.swift. */"]
lines += keys.map { key in
let value = transform
? (key.applyingTransform(StringTransform(rawValue: "Hans-Hant"), reverse: false) ?? key)
: key
return "\"\(key)\" = \"\(value)\";"
}
let path = resources.appendingPathComponent("\(locale).lproj/Localizable.strings")
try FileManager.default.createDirectory(at: path.deletingLastPathComponent(), withIntermediateDirectories: true)
try (lines.joined(separator: "\n") + "\n").write(to: path, atomically: true, encoding: .utf8)
}
+33
View File
@@ -0,0 +1,33 @@
import Foundation
/// Chooses the app language from the first preferred language only. iOS normally
/// scans the whole preference list, which can select Chinese when Italian is first
/// and Chinese is second; this app intentionally falls back to English instead.
enum AppLocalization {
static func identifier(for preferredLanguages: [String]) -> String {
guard let first = preferredLanguages.first,
Locale(identifier: first).languageCode == "zh" else {
return "en"
}
return Bundle.preferredLocalizations(
from: ["zh-Hans", "zh-Hant"], forPreferences: [first]
).first ?? "zh-Hans"
}
static let identifier = identifier(for: Locale.preferredLanguages)
static let locale = Locale(identifier: identifier)
// An explicit .lproj bundle also covers String(localized:) calls made outside
// SwiftUI views, where the view's locale environment is not available.
private static let localizedBundle: Bundle = {
guard let path = Bundle.main.path(forResource: identifier, ofType: "lproj"),
let bundle = Bundle(path: path) else {
return .main
}
return bundle
}()
static func string(_ value: String.LocalizationValue) -> String {
String(localized: value, bundle: localizedBundle)
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ struct AppRemoteConfiguration: Decodable, Equatable {
let communityPromptClients: [String]
static let fallback = AppRemoteConfiguration(
latestVersion: "1.0.7",
latestVersion: "1.0.8",
minimumSupportedVersion: "1.0.0",
communityPromptClients: [
ThirdPartyProxyClient.surge.rawValue,
+2 -2
View File
@@ -13,8 +13,8 @@ enum CertificateAuthorityStoreError: LocalizedError {
var errorDescription: String? {
switch self {
case .invalidAuthority: return String(localized: "本地 CA 证书或私钥无效")
case let .keychain(status): return String(localized: "无法写入设备钥匙串(\(status))")
case .invalidAuthority: return AppLocalization.string("本地 CA 证书或私钥无效")
case let .keychain(status): return AppLocalization.string("无法写入设备钥匙串(\(status))")
}
}
}
+2 -2
View File
@@ -12,8 +12,8 @@ enum CoreBridgeError: LocalizedError {
var errorDescription: String? {
switch self {
case .generationFailed: return String(localized: "无法生成本地证书")
case .serverStartFailed: return String(localized: "无法启动本地证书服务")
case .generationFailed: return AppLocalization.string("无法生成本地证书")
case .serverStartFailed: return AppLocalization.string("无法启动本地证书服务")
}
}
}
+2 -2
View File
@@ -8,8 +8,8 @@ enum ProxyRuntimeMode: String, CaseIterable, Codable, Identifiable {
var displayName: String {
switch self {
case .localWiFi: return String(localized: "APP模式")
case .thirdParty: return String(localized: "第三方代理模式")
case .localWiFi: return AppLocalization.string("APP模式")
case .thirdParty: return AppLocalization.string("第三方代理模式")
}
}
}
+3 -3
View File
@@ -58,13 +58,13 @@ struct RuntimeLogEntry: Codable, Identifiable, Equatable {
}
private func localizedDiagnosticText(_ text: String) -> String {
String(localized: String.LocalizationValue(text))
AppLocalization.string(String.LocalizationValue(text))
}
private func localizedDiagnosticValue(_ value: String) -> String {
switch value {
case "true": return String(localized: "是")
case "false": return String(localized: "否")
case "true": return AppLocalization.string("是")
case "false": return AppLocalization.string("否")
default: return localizedDiagnosticText(value)
}
}
+11 -11
View File
@@ -25,25 +25,25 @@ enum ThirdPartyProxyError: LocalizedError, Equatable {
var errorDescription: String? {
switch self {
case .invalidResponse:
return String(localized: "第三方代理返回了无法识别的数据")
return AppLocalization.string("第三方代理返回了无法识别的数据")
case .moduleNotIntercepted:
return String(localized: "请求未被第三方代理模块拦截,请检查模块、MITM 和代理连接")
return AppLocalization.string("请求未被第三方代理模块拦截,请检查模块、MITM 和代理连接")
case .rejected(let message):
return message
case .coordinateMismatch:
return String(localized: "第三方代理保存的坐标与当前选点不一致")
return AppLocalization.string("第三方代理保存的坐标与当前选点不一致")
case .network(let message):
return String(localized: "第三方代理请求失败:\(message)")
return AppLocalization.string("第三方代理请求失败:\(message)")
}
}
var recoverySuggestion: String {
return String(localized: "检查模块、MITM、证书和代理/VPN连接")
return AppLocalization.string("检查模块、MITM、证书和代理/VPN连接")
}
static func recoverySuggestion(for error: Error) -> String {
(error as? Self)?.recoverySuggestion
?? String(localized: "检查模块、MITM、证书和代理/VPN连接")
?? AppLocalization.string("检查模块、MITM、证书和代理/VPN连接")
}
}
@@ -105,7 +105,7 @@ final class ThirdPartyProxyManager: ObservableObject {
randomRadius: RandomRadiusStore.shared.isEnabled ? RandomRadiusStore.shared.radius : 0
))
guard response.success else {
throw ThirdPartyProxyError.rejected(response.error ?? String(localized: "第三方代理拒绝保存坐标"))
throw ThirdPartyProxyError.rejected(response.error ?? AppLocalization.string("第三方代理拒绝保存坐标"))
}
guard let latitude = response.latitude,
let longitude = response.longitude,
@@ -126,7 +126,7 @@ final class ThirdPartyProxyManager: ObservableObject {
func clear() async throws {
let response = try await perform(action: .clear)
guard response.success else {
throw ThirdPartyProxyError.rejected(response.error ?? String(localized: "第三方代理清除坐标失败"))
throw ThirdPartyProxyError.rejected(response.error ?? AppLocalization.string("第三方代理清除坐标失败"))
}
activeSettings = nil
connectionState = .connected(active: false)
@@ -142,7 +142,7 @@ final class ThirdPartyProxyManager: ObservableObject {
if response.error?.contains("无已保存") == true {
return false
}
throw ThirdPartyProxyError.rejected(response.error ?? String(localized: "第三方代理查询失败"))
throw ThirdPartyProxyError.rejected(response.error ?? AppLocalization.string("第三方代理查询失败"))
}
private enum Action {
@@ -153,7 +153,7 @@ final class ThirdPartyProxyManager: ObservableObject {
private func perform(action: Action) async throws -> ThirdPartyProxySettingsResponse {
guard !isRequesting else {
throw ThirdPartyProxyError.rejected(String(localized: "已有第三方代理请求正在执行"))
throw ThirdPartyProxyError.rejected(AppLocalization.string("已有第三方代理请求正在执行"))
}
isRequesting = true
defer { isRequesting = false }
@@ -229,7 +229,7 @@ enum ThirdPartyProxyClient: String, CaseIterable, Identifiable {
}
var verificationText: String? {
self == .shadowrocket ? nil : String(localized: "配置已提供,尚未验证")
self == .shadowrocket ? nil : AppLocalization.string("配置已提供,尚未验证")
}
var moduleFileName: String {
+8 -8
View File
@@ -29,14 +29,14 @@ enum VerificationResult: Equatable, Identifiable {
/// Localized, user-facing title. `id` stays stable for routing/equality.
var localizedTitle: String {
switch self {
case .success: return String(localized: "成功")
case .proxyNotRunning: return String(localized: "代理未运行")
case .verificationInProgress: return String(localized: "已有验证正在进行")
case .verificationSuperseded: return String(localized: "验证已被新位置取代")
case .certNotTrusted: return String(localized: "证书未信任")
case .wifiProxyNotConfigured: return String(localized: "WiFi代理未配置")
case .coordinateWriteFailed: return String(localized: "坐标写入失败")
case .patchFailed: return String(localized: "改写验证失败")
case .success: return AppLocalization.string("成功")
case .proxyNotRunning: return AppLocalization.string("代理未运行")
case .verificationInProgress: return AppLocalization.string("已有验证正在进行")
case .verificationSuperseded: return AppLocalization.string("验证已被新位置取代")
case .certNotTrusted: return AppLocalization.string("证书未信任")
case .wifiProxyNotConfigured: return AppLocalization.string("WiFi代理未配置")
case .coordinateWriteFailed: return AppLocalization.string("坐标写入失败")
case .patchFailed: return AppLocalization.string("改写验证失败")
}
}
@@ -0,0 +1,12 @@
import XCTest
@testable import PaopaoLocationSpoofer
final class AppLocalizationTests: XCTestCase {
func testOnlyFirstPreferredLanguageSelectsTheAppLanguage() {
XCTAssertEqual(AppLocalization.identifier(for: ["it-IT", "zh-Hans-CN"]), "en")
XCTAssertEqual(AppLocalization.identifier(for: ["fr-FR", "zh-Hant-TW"]), "en")
XCTAssertEqual(AppLocalization.identifier(for: ["zh-Hans-CN", "it-IT"]), "zh-Hans")
XCTAssertEqual(AppLocalization.identifier(for: ["zh-Hant-TW", "en-US"]), "zh-Hant")
XCTAssertEqual(AppLocalization.identifier(for: []), "en")
}
}
@@ -62,7 +62,7 @@ final class AppRemoteConfigurationTests: XCTestCase {
func testFallbackMatchesCurrentProjectPolicy() {
let configuration = AppRemoteConfiguration.fallback
XCTAssertEqual(configuration.latestVersion, "1.0.7")
XCTAssertEqual(configuration.latestVersion, "1.0.8")
XCTAssertEqual(configuration.minimumSupportedVersion, "1.0.0")
XCTAssertFalse(configuration.requestsCommunityPrompt(for: .shadowrocket))
for client in ThirdPartyProxyClient.allCases where client != .shadowrocket {
@@ -139,7 +139,7 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
XCTAssertNil(ThirdPartyProxyClient.shadowrocket.verificationText)
XCTAssertEqual(
ThirdPartyProxyClient.surge.verificationText,
String(localized: "配置已提供,尚未验证")
AppLocalization.string("配置已提供,尚未验证")
)
XCTAssertEqual(ThirdPartyProxyClient.egern.subscriptionURL, ThirdPartyProxyClient.surge.subscriptionURL)
XCTAssertTrue(ThirdPartyProxyClient.stash.subscriptionURL.absoluteString.hasPrefix(
+42 -1
View File
@@ -31,6 +31,8 @@ RUBY
for key in \
'环境信息' \
'复制 %@' \
'已复制 %@' \
'诊断日志' \
'======== 代理验证测试 ========' \
'======== 第三方代理连接检测 ========' \
@@ -44,7 +46,7 @@ grep -q 'localizedCategory' "$ROOT/Shared/RuntimeLog.swift" \
|| fail "runtime log categories must be localized when rendered"
grep -q 'localizedDetailsText' "$ROOT/App/DiagnosticsView.swift" \
|| fail "runtime log details must be localized in the diagnostics UI"
grep -q 'String(localized: "诊断日志")' "$ROOT/App/BugReportView.swift" \
grep -q 'AppLocalization.string("诊断日志")' "$ROOT/App/BugReportView.swift" \
|| fail "generated bug reports must localize their diagnostic section"
grep -q 'log(" " + e.localizedMessage)' "$ROOT/App/SetupCoordinator.swift" \
|| fail "bug-report verification logs must render stored messages in the active language"
@@ -55,4 +57,43 @@ if grep -R -n 'raw.githubusercontent.com/Yu9191/wloc' \
fail "deleted Yu9191/wloc repository must not remain a runtime dependency"
fi
# Check the real Foundation lookup path, not just the existence of locale files.
grep -q 'developmentLanguage: en' "$ROOT/project.yml" \
|| fail "English must be the development-language fallback"
grep -q ' - zh-Hant' "$ROOT/project.yml" \
|| fail "Traditional Chinese must be a supported region"
for locale in zh-Hans zh-Hant; do
plutil -lint "$ROOT/Resources/$locale.lproj/Localizable.strings" >/dev/null \
|| fail "$locale localization file is invalid"
plutil -lint "$ROOT/Resources/$locale.lproj/InfoPlist.strings" >/dev/null \
|| fail "$locale permission prompts are invalid"
done
ruby - "$STRINGS" "$ROOT/Resources/zh-Hans.lproj/Localizable.strings" \
"$ROOT/Resources/zh-Hant.lproj/Localizable.strings" <<'RUBY' || exit 1
paths = ARGV
keys = paths.map { |path| File.readlines(path).map { |line| line[/^"((?:\\.|[^"\\])*)"\s*=/, 1] }.compact }
abort "FAIL: Chinese tables must cover every English key" unless keys[1] == keys[0] && keys[2] == keys[0]
RUBY
grep -Fq '"虚拟定位" = "虚拟定位";' "$ROOT/Resources/zh-Hans.lproj/Localizable.strings" \
|| fail "Simplified Chinese must resolve the source key"
grep -Fq '"虚拟定位" = "虛擬定位";' "$ROOT/Resources/zh-Hant.lproj/Localizable.strings" \
|| fail "Traditional Chinese must use Traditional characters"
swift - <<'SWIFT' || fail "language selection differs from expected behavior"
import Foundation
let available = ["en", "zh-Hans", "zh-Hant"]
for (language, expected) in [
("zh-CN", "zh-Hans"), ("zh-TW", "zh-Hant"), ("zh-HK", "zh-Hant"),
("it-IT", "en"), ("fr-FR", "en")
] {
let selected = Bundle.preferredLocalizations(from: available, forPreferences: [language]).first
guard selected == expected else {
fputs("FAIL: \(language) selected \(selected ?? "nil")\n", stderr)
exit(1)
}
}
SWIFT
echo "PASS: localization and diagnostic export contract"
+5 -5
View File
@@ -10,8 +10,8 @@ CONTENT="$ROOT/App/ContentView.swift"
SETUP="$ROOT/App/FirstSetupView.swift"
SETTINGS="$ROOT/App/SettingsView.swift"
grep -q 'String(localized: "APP模式")' "$MODE" || fail "localized APP mode display name is missing"
grep -q 'String(localized: "第三方代理模式")' "$MODE" || fail "localized third-party mode display name is missing"
grep -q 'AppLocalization.string("APP模式")' "$MODE" || fail "localized APP mode display name is missing"
grep -q 'AppLocalization.string("第三方代理模式")' "$MODE" || fail "localized third-party mode display name is missing"
grep -q 'hasSelectedMode' "$MODE" || fail "first-launch mode selection must be persisted"
grep -q 'guard runtimeMode.hasSelectedMode else' "$CONTENT" || fail "mode selection must gate startup"
grep -q 'phase = .setup' "$CONTENT" || fail "first launch must enter setup before map construction"
@@ -106,7 +106,7 @@ grep -q 'setup.requestThirdPartySetup(message: error.localizedDescription)' "$RO
|| fail "third-party coordinate sync failures must open the import guide"
grep -q '检测到第三方代理连接异常,请检查模块、MITM 和代理连接后重新检测' "$SETUP" \
|| fail "runtime repair must explain why the import guide opened"
test "$(grep -c 'title: String(localized: \"接口连接失败\")' "$SETUP")" -eq 1 \
test "$(grep -c 'title: AppLocalization.string(\"接口连接失败\")' "$SETUP")" -eq 1 \
|| fail "third-party failure details must render in one shared result area"
grep -Fq '当前客户端:\(client.name)' "$SETUP" \
|| fail "third-party failure logs must identify the selected client"
@@ -142,7 +142,7 @@ test -s "$ROOT/docs/onboarding-screenshots/shadowrocket/shadowrocket-module-impo
grep -q 'presentSuccessfulOperationTip(.activation)' "$ROOT/App/MapHomeView.swift" || fail "third-party save must present the activation tip"
grep -q 'presentSuccessfulOperationTip(.deactivation)' "$ROOT/App/MapHomeView.swift" || fail "third-party clear must present the deactivation tip"
grep -q 'if spoofState == .active' "$ROOT/App/MapHomeView.swift" || fail "manual help must follow the shared spoof state"
grep -q 'MARKETING_VERSION: "1.0.7"' "$ROOT/project.yml" || fail "marketing version must be 1.0.7"
grep -q 'CURRENT_PROJECT_VERSION: "8"' "$ROOT/project.yml" || fail "build version must be 8"
grep -q 'MARKETING_VERSION: "1.0.8"' "$ROOT/project.yml" || fail "marketing version must be 1.0.8"
grep -q 'CURRENT_PROJECT_VERSION: "9"' "$ROOT/project.yml" || fail "build version must be 9"
echo "PASS: third-party proxy mode contract"
@@ -25,7 +25,7 @@ import sys
with open(sys.argv[1], encoding="utf-8") as handle:
config = json.load(handle)
assert config["latestVersion"] == "1.0.7"
assert config["latestVersion"] == "1.0.8"
assert config["minimumSupportedVersion"] == "1.0.0"
assert "shadowrocket" not in config["communityPromptClients"]
assert set(config["communityPromptClients"]) == {
@@ -130,7 +130,7 @@ grep -Fq 'SafariView(url: destination.url)' "$BUG_REPORT" \
|| fail "the App bug report must not be handed to an external GitHub client"
grep -Fq 'App 生成的诊断报告' "$BUG_REPORT" \
|| fail "the App must tell users where to paste the generated report"
grep -Fq 'String(localized: "第三方客户端")' "$BUG_REPORT" \
grep -Fq 'AppLocalization.string("第三方客户端")' "$BUG_REPORT" \
|| fail "the generated report must identify the selected third-party client"
grep -Fq 'Label("报告 Bug"' "$SETTINGS" \
|| fail "Settings must identify the support action as a bug report"
+1
View File
@@ -4,6 +4,7 @@
## 已发布
- [v1.0.8](https://github.com/xweiba/location-spoofer/releases/tag/v1.0.8) — 2026-09-24
- [v1.0.7](https://github.com/xweiba/location-spoofer/releases/tag/v1.0.7) — 2026-09-11
- [v1.0.6](https://github.com/xweiba/location-spoofer/releases/tag/v1.0.6) — 2026-09-01
- [v1.0.5](https://github.com/xweiba/location-spoofer/releases/tag/v1.0.5) — 2026-08-10
+5 -4
View File
@@ -4,18 +4,19 @@ options:
deploymentTarget:
iOS: "15.0"
createIntermediateGroups: true
developmentLanguage: zh-Hans
developmentLanguage: en
knownRegions:
- zh-Hans
- en
- zh-Hans
- zh-Hant
- Base
settings:
base:
SWIFT_VERSION: "5.9"
MARKETING_VERSION: "1.0.7"
CURRENT_PROJECT_VERSION: "8"
MARKETING_VERSION: "1.0.8"
CURRENT_PROJECT_VERSION: "9"
CODE_SIGN_STYLE: Manual
CODE_SIGNING_ALLOWED: "NO"
CODE_SIGNING_REQUIRED: "NO"
+1 -1
View File
@@ -1,5 +1,5 @@
{
"latestVersion": "1.0.7",
"latestVersion": "1.0.8",
"minimumSupportedVersion": "1.0.0",
"communityPromptClients": ["surge", "quantumultX", "loon", "stash", "egern"]
}