mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-26 00:21:55 +08:00
fix(i18n): 完善国际化并迁移 WLOC 脚本
This commit is contained in:
@@ -73,9 +73,13 @@ final class RealtimeLocationManagerTests: XCTestCase {
|
||||
func testOneShotTimeoutTransitionsToContinuousFallback() async {
|
||||
let driver = FakeRealtimeLocationDriver()
|
||||
let manager = RealtimeLocationManager(driver: driver, oneShotTimeoutNanoseconds: 5_000_000, fallbackTimeoutNanoseconds: 1_000_000_000)
|
||||
let fallbackStarted = expectation(description: "Continuous location fallback started")
|
||||
driver.onStartUpdating = {
|
||||
fallbackStarted.fulfill()
|
||||
}
|
||||
|
||||
let request = Task { await manager.requestLocation() }
|
||||
try? await Task.sleep(nanoseconds: 20_000_000)
|
||||
await fulfillment(of: [fallbackStarted], timeout: 1)
|
||||
XCTAssertEqual(driver.startUpdatingCallCount, 1)
|
||||
|
||||
driver.emit(CLLocation(latitude: 39.90, longitude: 116.40))
|
||||
@@ -148,6 +152,7 @@ private final class FakeRealtimeLocationDriver: RealtimeLocationDriving {
|
||||
var authorizationStatus: CLAuthorizationStatus = .authorizedWhenInUse
|
||||
weak var delegate: CLLocationManagerDelegate?
|
||||
var onRequestLocation: (() -> Void)?
|
||||
var onStartUpdating: (() -> Void)?
|
||||
private(set) var requestAuthorizationCallCount = 0
|
||||
private(set) var requestLocationCallCount = 0
|
||||
private(set) var startUpdatingCallCount = 0
|
||||
@@ -164,6 +169,7 @@ private final class FakeRealtimeLocationDriver: RealtimeLocationDriving {
|
||||
|
||||
func startUpdatingLocation() {
|
||||
startUpdatingCallCount += 1
|
||||
onStartUpdating?()
|
||||
}
|
||||
|
||||
func stopUpdatingLocation() {
|
||||
|
||||
@@ -125,16 +125,19 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testClientLinksUseUpstreamModulesAndVerificationLabels() {
|
||||
func testClientLinksUseProjectOwnedModulesAndVerificationLabels() {
|
||||
XCTAssertEqual(
|
||||
ThirdPartyProxyManager.interceptionHostnamesText,
|
||||
"gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com"
|
||||
)
|
||||
XCTAssertNil(ThirdPartyProxyClient.shadowrocket.verificationText)
|
||||
XCTAssertTrue(ThirdPartyProxyClient.surge.verificationText?.contains("尚未验证") == true)
|
||||
XCTAssertEqual(
|
||||
ThirdPartyProxyClient.surge.verificationText,
|
||||
String(localized: "配置已提供,尚未验证")
|
||||
)
|
||||
XCTAssertEqual(ThirdPartyProxyClient.egern.subscriptionURL, ThirdPartyProxyClient.surge.subscriptionURL)
|
||||
XCTAssertTrue(ThirdPartyProxyClient.stash.subscriptionURL.absoluteString.hasPrefix(
|
||||
"https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/"
|
||||
"https://gh-proxy.org/https://raw.githubusercontent.com/xweiba/location-spoofer/main/Resources/ThirdPartyProxyModules/"
|
||||
))
|
||||
let stashComponents = URLComponents(
|
||||
url: ThirdPartyProxyClient.stash.subscriptionURL,
|
||||
@@ -144,10 +147,10 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
|
||||
url: ThirdPartyProxyClient.shadowrocket.subscriptionURL,
|
||||
resolvingAgainstBaseURL: false
|
||||
)
|
||||
XCTAssertEqual(stashComponents?.path, "/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/wloc.stoverride")
|
||||
XCTAssertEqual(shadowrocketComponents?.path, "/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/wloc.module")
|
||||
XCTAssertTrue(stashComponents?.queryItems?.isEmpty ?? true)
|
||||
XCTAssertTrue(shadowrocketComponents?.queryItems?.isEmpty ?? true)
|
||||
XCTAssertEqual(stashComponents?.path, "/https://raw.githubusercontent.com/xweiba/location-spoofer/main/Resources/ThirdPartyProxyModules/wloc.stoverride")
|
||||
XCTAssertEqual(shadowrocketComponents?.path, "/https://raw.githubusercontent.com/xweiba/location-spoofer/main/Resources/ThirdPartyProxyModules/wloc.module")
|
||||
XCTAssertEqual(stashComponents?.queryItems?.first?.value, ThirdPartyProxyClient.moduleSubscriptionVersion)
|
||||
XCTAssertEqual(shadowrocketComponents?.queryItems?.first?.value, ThirdPartyProxyClient.moduleSubscriptionVersion)
|
||||
XCTAssertEqual(ThirdPartyProxyClient.shadowrocket.launchURL?.scheme, "shadowrocket")
|
||||
XCTAssertEqual(ThirdPartyProxyClient.surge.launchURL?.scheme, "surge")
|
||||
XCTAssertEqual(ThirdPartyProxyClient.quantumultX.launchURL?.scheme, "quantumult-x")
|
||||
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
STRINGS="$ROOT/Resources/en.lproj/Localizable.strings"
|
||||
|
||||
fail() {
|
||||
echo "FAIL: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
plutil -lint "$STRINGS" >/dev/null || fail "English localization file is invalid"
|
||||
|
||||
ruby - "$STRINGS" <<'RUBY' || exit 1
|
||||
path = ARGV.fetch(0)
|
||||
entries = File.readlines(path).map do |line|
|
||||
match = line.match(/^"((?:\\.|[^"])*)"\s*=\s*"((?:\\.|[^"])*)";/)
|
||||
[match[1], match[2]] if match
|
||||
end.compact
|
||||
|
||||
duplicates = entries.group_by(&:first).select { |_key, values| values.length > 1 }.keys
|
||||
abort "FAIL: duplicate English localization keys: #{duplicates.join(', ')}" unless duplicates.empty?
|
||||
|
||||
placeholder = /%(?:\d+\$)?(?:@|lld|ld|d|f)/
|
||||
entries.each do |source, target|
|
||||
source_count = source.scan(placeholder).length
|
||||
target_count = target.scan(placeholder).length
|
||||
abort "FAIL: placeholder mismatch for #{source.inspect}" unless source_count == target_count
|
||||
end
|
||||
RUBY
|
||||
|
||||
for key in \
|
||||
'环境信息' \
|
||||
'诊断日志' \
|
||||
'======== 代理验证测试 ========' \
|
||||
'======== 第三方代理连接检测 ========' \
|
||||
'======== 第三方代理运行检测 ========' \
|
||||
'第三方代理测试模式:模块连接成功;已保存坐标=%@' \
|
||||
'成功:{\"success\":true,\"longitude\":113.0,\"latitude\":22.0,\"accuracy\":25}\n失败:{\"success\":false,\"error\":\"错误说明\"}'; do
|
||||
grep -Fq "\"$key\" = " "$STRINGS" || fail "missing critical English localization: $key"
|
||||
done
|
||||
|
||||
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" \
|
||||
|| 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"
|
||||
|
||||
if grep -R -n 'raw.githubusercontent.com/Yu9191/wloc' \
|
||||
"$ROOT/App" "$ROOT/Shared" "$ROOT/Resources/ThirdPartyProxyModules" \
|
||||
"$ROOT/ThirdParty/WlocScripts/modules"; then
|
||||
fail "deleted Yu9191/wloc repository must not remain a runtime dependency"
|
||||
fi
|
||||
|
||||
echo "PASS: localization and diagnostic export contract"
|
||||
@@ -10,8 +10,8 @@ CONTENT="$ROOT/App/ContentView.swift"
|
||||
SETUP="$ROOT/App/FirstSetupView.swift"
|
||||
SETTINGS="$ROOT/App/SettingsView.swift"
|
||||
|
||||
grep -q 'return "APP模式"' "$MODE" || fail "APP mode display name is missing"
|
||||
grep -q 'return "第三方代理模式"' "$MODE" || fail "third-party mode display name is missing"
|
||||
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 '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"
|
||||
@@ -37,8 +37,23 @@ grep -Fq '保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>' "$SETUP" \
|
||||
grep -Fq '清除:GET ?action=clear' "$SETUP" \
|
||||
|| fail "client integration guidance must document the clear action"
|
||||
|
||||
grep -q 'Yu9191/wloc/refs/heads/main/modules' "$MANAGER" \
|
||||
|| fail "third-party subscription must point at upstream Yu9191 modules"
|
||||
grep -q 'raw.githubusercontent.com/xweiba/location-spoofer/main' "$MANAGER" \
|
||||
|| fail "third-party subscription must point at project-owned modules"
|
||||
! grep -q 'raw.githubusercontent.com/Yu9191/wloc' "$MANAGER" \
|
||||
|| fail "third-party subscription must not depend on the removed upstream repository"
|
||||
MODULES="$ROOT/Resources/ThirdPartyProxyModules"
|
||||
SCRIPTS="$ROOT/ThirdParty/WlocScripts"
|
||||
for file in wloc.module wloc.sgmodule wloc.conf wloc.lpx wloc.stoverride; do
|
||||
test -s "$MODULES/$file" || fail "missing project-owned mirrored module: $file"
|
||||
test -s "$SCRIPTS/modules/direct/$file" || fail "missing project-owned direct module: $file"
|
||||
! grep 'ThirdParty/WlocScripts/dist/v1/wloc' "$MODULES/$file" "$SCRIPTS/modules/direct/$file" \
|
||||
| grep -Fvq '?v=1.0.7' \
|
||||
|| fail "hosted script URLs must carry the current cache version: $file"
|
||||
done
|
||||
test -s "$SCRIPTS/dist/v1/wloc.js" || fail "missing hosted WLOC response script"
|
||||
test -s "$SCRIPTS/dist/v1/wloc-settings.js" || fail "missing hosted WLOC settings script"
|
||||
! grep -R -q 'raw.githubusercontent.com/Yu9191/wloc' "$MODULES" "$SCRIPTS/modules/direct" \
|
||||
|| fail "hosted modules must not reference the removed upstream repository"
|
||||
grep -q 'wloc.sgmodule' "$MANAGER" || fail "Surge/Egern module mapping is missing"
|
||||
grep -q 'wloc.stoverride' "$MANAGER" || fail "Stash must use .stoverride directly"
|
||||
grep -q 'shadowrocket://' "$MANAGER" || fail "Shadowrocket launch URL is missing"
|
||||
@@ -88,7 +103,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: \"接口连接失败\"' "$SETUP")" -eq 1 \
|
||||
test "$(grep -c 'title: String(localized: \"接口连接失败\")' "$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"
|
||||
|
||||
@@ -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.5"
|
||||
assert config["latestVersion"] == "1.0.6"
|
||||
assert config["minimumSupportedVersion"] == "1.0.0"
|
||||
assert "shadowrocket" not in config["communityPromptClients"]
|
||||
assert set(config["communityPromptClients"]) == {
|
||||
@@ -121,7 +121,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 '第三方客户端:' "$BUG_REPORT" \
|
||||
grep -Fq 'String(localized: "第三方客户端")' "$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"
|
||||
|
||||
Reference in New Issue
Block a user