fix(update): 按语言加载三语更新日志并重发 v1.0.8

This commit is contained in:
xweiba
2026-09-24 23:36:23 +08:00
parent ca239411ee
commit 4aa4c6aa6c
14 changed files with 178 additions and 24 deletions
+20 -10
View File
@@ -40,16 +40,26 @@ jobs:
- name: Validate archived release notes
run: |
VERSION="${{ github.ref_name }}"
NOTES_FILE="docs/releases/${VERSION}.md"
if [ ! -s "$NOTES_FILE" ]; then
echo "::error::缺少版本归档 $NOTES_FILE。请在打 tag 前运行 ./Scripts/generate-release-notes.sh $VERSION 并提交。"
exit 1
fi
if ! grep -Fq "# ${VERSION}" "$NOTES_FILE"; then
echo "::error::$NOTES_FILE 的标题必须包含 # ${VERSION}"
exit 1
fi
cp "$NOTES_FILE" /tmp/release_notes.md
for NOTES_FILE in \
"docs/releases/${VERSION}.en.md" \
"docs/releases/${VERSION}.md" \
"docs/releases/${VERSION}.zh-Hant.md"; do
if [ ! -s "$NOTES_FILE" ]; then
echo "::error::Missing localized release notes: $NOTES_FILE"
exit 1
fi
if ! grep -Fq "# ${VERSION}" "$NOTES_FILE"; then
echo "::error::$NOTES_FILE must contain the # ${VERSION} heading"
exit 1
fi
done
{
cat "docs/releases/${VERSION}.en.md"
printf '\n---\n\n'
cat "docs/releases/${VERSION}.md"
printf '\n---\n\n'
cat "docs/releases/${VERSION}.zh-Hant.md"
} > /tmp/release_notes.md
- name: Create Release
uses: softprops/action-gh-release@v2
+2 -2
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.8-2563EB)](docs/CHANGELOG.md)
[![Version](https://img.shields.io/badge/version-v1.0.8-2563EB)](docs/CHANGELOG.en.md)
[Features](#feature-overview) ·
[How It Works](#how-it-works) ·
@@ -415,7 +415,7 @@ Paste it into the **App-generated diagnostic report** field in the GitHub Issue
- [Build guide](docs/BUILD.md)
- [Third-party module documentation](docs/THIRD_PARTY_MODULES.md)
- [Changelog](docs/CHANGELOG.md)
- [Changelog](docs/CHANGELOG.en.md)
- [中文文档](README.zh-CN.md)
- [GitHub Issues](https://github.com/xweiba/location-spoofer/issues)
+1
View File
@@ -49,3 +49,4 @@ ${COMMITS}
EOF
echo "Generated $OUTPUT from $RANGE"
echo "Before tagging, add docs/releases/${VERSION}.en.md and docs/releases/${VERSION}.zh-Hant.md."
+20 -7
View File
@@ -153,11 +153,13 @@ enum AppRemoteConfigurationService {
return nil
}
static func fetchReleaseNotes(version: String) async -> String? {
static func fetchReleaseNotes(
version: String, language: String = AppLocalization.identifier
) async -> String? {
let session = makeSession()
defer { session.finishTasksAndInvalidate() }
for url in releaseNotesURLs(version: version) {
for url in releaseNotesURLs(version: version, language: language) {
var request = URLRequest(url: url)
request.timeoutInterval = 1.5
@@ -166,7 +168,7 @@ enum AppRemoteConfigurationService {
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200,
let markdown = String(data: data, encoding: .utf8),
let summary = releaseNotesSummary(markdown) else {
let summary = releaseNotesSummary(markdown, language: language) else {
continue
}
return summary
@@ -185,8 +187,17 @@ enum AppRemoteConfigurationService {
return nil
}
static func releaseNotesURLs(version: String) -> [URL] {
let path = "https://raw.githubusercontent.com/xweiba/location-spoofer/main/docs/releases/v\(version).md"
static func releaseNotesURLs(
version: String, language: String = AppLocalization.identifier
) -> [URL] {
// The unsuffixed archive remains Simplified Chinese for older clients.
let suffix: String
switch language {
case "zh-Hans": suffix = ""
case "zh-Hant": suffix = ".zh-Hant"
default: suffix = ".en"
}
let path = "https://raw.githubusercontent.com/xweiba/location-spoofer/main/docs/releases/v\(version)\(suffix).md"
return [
URL(string: "https://gh-proxy.org/\(path)")!,
URL(string: path)!
@@ -202,12 +213,14 @@ enum AppRemoteConfigurationService {
return URLSession(configuration: sessionConfiguration)
}
private static func releaseNotesSummary(_ markdown: String) -> String? {
static func releaseNotesSummary(_ markdown: String, language: String) -> String? {
var items: [String] = []
var inMainUpdates = false
for rawLine in markdown.components(separatedBy: .newlines) {
let line = rawLine.trimmingCharacters(in: .whitespacesAndNewlines)
if line == "## 主要更新" {
let heading = language == "zh-Hans" || language == "zh-Hant"
? "## 主要更新" : "## Highlights"
if line == heading {
inMainUpdates = true
continue
}
@@ -77,8 +77,44 @@ final class AppRemoteConfigurationTests: XCTestCase {
"raw.githubusercontent.com"
)
let releaseNotesURLs = AppRemoteConfigurationService.releaseNotesURLs(version: "1.0.5")
let releaseNotesURLs = AppRemoteConfigurationService.releaseNotesURLs(version: "1.0.8", language: "en")
XCTAssertEqual(releaseNotesURLs.first?.host, "gh-proxy.org")
XCTAssertEqual(releaseNotesURLs.last?.host, "raw.githubusercontent.com")
XCTAssertTrue(releaseNotesURLs.last?.path.hasSuffix("/docs/releases/v1.0.8.en.md") == true)
XCTAssertTrue(AppRemoteConfigurationService.releaseNotesURLs(
version: "1.0.8", language: "zh-Hans"
).last?.path.hasSuffix("/docs/releases/v1.0.8.md") == true)
XCTAssertTrue(AppRemoteConfigurationService.releaseNotesURLs(
version: "1.0.8", language: "zh-Hant"
).last?.path.hasSuffix("/docs/releases/v1.0.8.zh-Hant.md") == true)
XCTAssertTrue(AppRemoteConfigurationService.releaseNotesURLs(
version: "1.0.8", language: "it"
).last?.path.hasSuffix("/docs/releases/v1.0.8.en.md") == true)
}
func testReleaseNotesSummaryUsesTheSelectedLanguageHeading() {
let english = "# v1.0.8\n\n## Highlights\n\n- English update\n\n## Installation"
let chinese = "# v1.0.8\n\n## 主要更新\n\n- 中文更新\n\n## 安装"
XCTAssertEqual(AppRemoteConfigurationService.releaseNotesSummary(english, language: "en"), "• English update")
XCTAssertEqual(AppRemoteConfigurationService.releaseNotesSummary(chinese, language: "zh-Hans"), "• 中文更新")
XCTAssertEqual(AppRemoteConfigurationService.releaseNotesSummary(chinese, language: "zh-Hant"), "• 中文更新")
XCTAssertNil(AppRemoteConfigurationService.releaseNotesSummary(chinese, language: "en"))
XCTAssertEqual(AppRemoteConfigurationService.releaseNotesSummary(english, language: "it"), "• English update")
}
func testArchivedReleaseNotesParseInEachLanguage() throws {
let root = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
for (language, name) in [
("en", "v1.0.8.en.md"),
("zh-Hans", "v1.0.8.md"),
("zh-Hant", "v1.0.8.zh-Hant.md")
] {
let markdown = try String(
contentsOf: root.appendingPathComponent("docs/releases/\(name)"),
encoding: .utf8
)
XCTAssertNotNil(AppRemoteConfigurationService.releaseNotesSummary(markdown, language: language))
}
}
}
+1 -1
View File
@@ -143,6 +143,6 @@ grep -q 'presentSuccessfulOperationTip(.activation)' "$ROOT/App/MapHomeView.swif
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.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"
grep -q 'CURRENT_PROJECT_VERSION: "10"' "$ROOT/project.yml" || fail "build version must be 10"
echo "PASS: third-party proxy mode contract"
+10 -2
View File
@@ -54,8 +54,16 @@ grep -q 'static let configurationURLs = \[' "$CONFIG" \
|| fail "update detection must retain multiple configuration sources"
! grep -q 'data.count' "$CONFIG" \
|| fail "the client must not impose a remote configuration file-size limit"
grep -q 'docs/releases/v\\(version).md' "$CONFIG" \
|| fail "update notes must come from the archived release document"
grep -Fq 'docs/releases/v\(version)\(suffix).md' "$CONFIG" \
|| fail "update notes must come from the language-specific archived release document"
for locale in '' .en .zh-Hant; do
test -s "$ROOT/docs/releases/v${LATEST_VERSION}${locale}.md" \
|| fail "missing localized v${LATEST_VERSION}${locale} release notes"
done
grep -Fq '## Highlights' "$ROOT/docs/releases/v${LATEST_VERSION}.en.md" \
|| fail "English update highlights are missing"
grep -Fq '## 主要更新' "$ROOT/docs/releases/v${LATEST_VERSION}.zh-Hant.md" \
|| fail "Traditional Chinese update highlights are missing"
grep -q '/releases/latest' "$CONFIG" \
|| fail "missing release notes must fall back to the latest Release page"
grep -q '.task { await checkForUpdates() }' "$CONTENT" \
+11
View File
@@ -0,0 +1,11 @@
# Changelog
Release notes are archived under [`docs/releases/`](releases/).
## Releases
- [v1.0.8](releases/v1.0.8.en.md) — September 24, 2026 ([简体中文](releases/v1.0.8.md) · [繁體中文](releases/v1.0.8.zh-Hant.md))
- [v1.0.7](releases/v1.0.7.md) — September 11, 2026 (Chinese)
- [v1.0.6](releases/v1.0.6.md) — September 1, 2026 (Chinese)
Earlier releases: [GitHub Releases](https://github.com/xweiba/location-spoofer/releases).
+2
View File
@@ -2,6 +2,8 @@
各版本发布说明见 `docs/releases/` 目录。
[English](CHANGELOG.en.md) · [繁體中文](CHANGELOG.zh-Hant.md)
## 已发布
- [v1.0.8](https://github.com/xweiba/location-spoofer/releases/tag/v1.0.8) — 2026-09-24
+11
View File
@@ -0,0 +1,11 @@
# 更新紀錄
各版本的說明存放於 [`docs/releases/`](releases/) 目錄。
## 已發佈
- [v1.0.8](releases/v1.0.8.zh-Hant.md) — 2026-09-24([English](releases/v1.0.8.en.md) · [简体中文](releases/v1.0.8.md))
- [v1.0.7](releases/v1.0.7.md) — 2026-09-11(簡體中文)
- [v1.0.6](releases/v1.0.6.md) — 2026-09-01(簡體中文)
更早的版本:[GitHub Releases](https://github.com/xweiba/location-spoofer/releases)。
+29
View File
@@ -0,0 +1,29 @@
# v1.0.8
Release date: September 24, 2026
[简体中文](https://github.com/xweiba/location-spoofer/blob/v1.0.8/docs/releases/v1.0.8.md) · [繁體中文](https://github.com/xweiba/location-spoofer/blob/v1.0.8/docs/releases/v1.0.8.zh-Hant.md)
This re-release replaces build 9 with **build 10** while keeping the v1.0.8 version.
## Highlights
- Fixed the app showing Chinese when a non-Chinese language is preferred: the app now uses only the **first** preferred language. Simplified and Traditional Chinese display Chinese; Italian and other non-Chinese languages display English even if Chinese is second in the preference list.
- Added explicit Simplified and Traditional Chinese resources and made English the development-language fallback. Diagnostics, prompts, and generated bug reports follow the same language choice.
- Added missing English translations for two hostname-copy messages and regression tests for language selection.
- Made English the default repository README, retained a separate Chinese README, added the project's AGPL-3.0 license, and preserved third-party script license and provenance information.
- Added contract tests to CI and synchronized the remote-version fallback with the release version.
## Changes since v1.0.7
- `5b9fedf` Fix first-preferred-language fallback and prepare v1.0.8
- `228b67b` Make English the default README and add AGPL licensing
- `f4712f7` Run contract tests in CI and via make test
- `393c7cc` Align the built-in fallback version and add consistency checks
## Self-signing and installation
- The Release attachment is an **unsigned IPA**. Sign it before installing it on an iPhone.
- A free Apple ID and Impactor can be used; a paid developer account is not required.
- Keep the Bundle ID `com.paopaolabs.location-spoofer`, App Group `group.com.paopaolabs.location-spoofer`, and existing entitlements when signing.
- Free Apple ID signing usually expires after seven days, after which the app needs to be signed and installed again.
+4
View File
@@ -2,6 +2,10 @@
发布日期:2026-09-24
[English](https://github.com/xweiba/location-spoofer/blob/v1.0.8/docs/releases/v1.0.8.en.md) · [繁體中文](https://github.com/xweiba/location-spoofer/blob/v1.0.8/docs/releases/v1.0.8.zh-Hant.md)
本次重新发布以**构建号 10** 替换原构建号 9,版本号仍为 v1.0.8。
## 主要更新
- 修复非中文系统语言下错误显示中文的问题:只根据首选语言的**第一项**选择 App 界面语言。简体中文和繁体中文分别显示中文;意大利语及其他非中文语言统一显示英文,即使中文排在首选语言列表的后面。
+29
View File
@@ -0,0 +1,29 @@
# v1.0.8
發佈日期:2026-09-24
[English](https://github.com/xweiba/location-spoofer/blob/v1.0.8/docs/releases/v1.0.8.en.md) · [简体中文](https://github.com/xweiba/location-spoofer/blob/v1.0.8/docs/releases/v1.0.8.md)
本次重新發佈以**建置版本 10** 取代原有的建置版本 9,版本號仍為 v1.0.8。
## 主要更新
- 修正非中文系統語言下錯誤顯示中文的問題:App 現在只根據偏好語言的**第一項**選擇介面語言。簡體和繁體中文顯示中文;義大利文及其他非中文語言顯示英文,即使中文排在語言清單的第二項。
- 補齊簡體與繁體中文本地化資源,並將英文設為開發語言及預設回退語言。診斷資訊、提示與產生的 Bug 回報也遵循相同的語言選擇規則。
- 補上兩處主機名稱複製提示的英文翻譯,並加入語言選擇的迴歸測試。
- 將英文設為儲存庫的預設 README,保留獨立的中文文件;加入專案的 AGPL-3.0 授權,並保留第三方腳本原有的授權及來源說明。
- 將契約測試加入 CI,並統一遠端版本設定與內建回退版本號。
## 提交變更摘要
- `5b9fedf` 修正偏好語言回退邏輯並準備 v1.0.8
- `228b67b` 將英文 README 設為預設並加入 AGPL 授權
- `f4712f7` 在 CI 與 make test 執行契約測試
- `393c7cc` 修正內建回退版本號並加入一致性檢查
## 自行簽署與安裝
- Release 附件是**未簽署的 IPA**,安裝到 iPhone 前必須先簽署。
- 可以使用免費 Apple ID 和 Impactor 簽署安裝,無須付費開發者帳號。
- 簽署時請保留 Bundle ID `com.paopaolabs.location-spoofer`、App Group `group.com.paopaolabs.location-spoofer` 及原有 entitlements。
- 免費 Apple ID 的簽署通常只有七天有效期,到期後需要重新簽署並安裝。
+1 -1
View File
@@ -16,7 +16,7 @@ settings:
base:
SWIFT_VERSION: "5.9"
MARKETING_VERSION: "1.0.8"
CURRENT_PROJECT_VERSION: "9"
CURRENT_PROJECT_VERSION: "10"
CODE_SIGN_STYLE: Manual
CODE_SIGNING_ALLOWED: "NO"
CODE_SIGNING_REQUIRED: "NO"