Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
13c1194
Update xcodebuild command to specify destination
jlj1102 Feb 17, 2026
cd234ce
Update xcodebuild command to specify destination
jlj1102 Feb 17, 2026
c8c8ea8
Update build.yml
jlj1102 Feb 17, 2026
2980b53
Update Xcode version in build workflow
jlj1102 Feb 17, 2026
0a56e76
Translate UI text to Chinese in SettingsView
jlj1102 Feb 17, 2026
2337ad9
Translate UI text to Chinese
jlj1102 Feb 17, 2026
02e1856
Translate login error messages to Chinese
jlj1102 Feb 17, 2026
0a91a37
Translate some error messages to Chinese in AnisetteDataHelper
jlj1102 Feb 17, 2026
f76c032
Update logging messages to Chinese
jlj1102 Feb 17, 2026
fd1908c
Fix error message syntax in getAnisetteData
jlj1102 Feb 17, 2026
42ba873
Mention Chinese translation in README
jlj1102 Feb 17, 2026
108e017
Update tab labels to support localization
jlj1102 Feb 17, 2026
18e2354
Add files via upload
jlj1102 Feb 17, 2026
ac959dd
Rename Untitled 2.png to icon.png
jlj1102 Feb 17, 2026
9f5ad8b
Dark mode icon ready — preparing
jlj1102 Feb 17, 2026
08bea94
Add filenames for app icons in Contents.json
jlj1102 Feb 17, 2026
2802f50
Add transparent icon
jlj1102 Feb 17, 2026
578fd5b
Rename IMG_0156.png to transicon.png
jlj1102 Feb 17, 2026
39b6e60
Update Contents.json
jlj1102 Feb 17, 2026
f6f8fde
Replace
jlj1102 Feb 17, 2026
63c7c3c
Replacement 2
jlj1102 Feb 17, 2026
22b59ea
Update app icon filename in Contents.json
jlj1102 Feb 17, 2026
b48a165
Add Chinese translation and usage instructions
jlj1102 Feb 17, 2026
7a242c9
Update credits section with Chinese translations
jlj1102 Feb 17, 2026
b413c6e
Fix formatting and improve Chinese translation section
jlj1102 Feb 17, 2026
165486e
Add debug step for xcodebuild in build.yml
jlj1102 Feb 18, 2026
3847eeb
feat: added multilang test
jlj1102 Feb 18, 2026
a3678fc
fix: some bugs xcodebuild cannot work #1
jlj1102 Feb 18, 2026
1a52cdb
debug: check project status
jlj1102 Feb 18, 2026
b655d15
fix: Attempt #2
jlj1102 Feb 18, 2026
e2904af
feat: mentioning the success of multilang support in README.md
jlj1102 Feb 18, 2026
292a5cd
feat: Half-made multilang
jlj1102 Feb 18, 2026
e0b48d2
fix: A stupid missing ";"
jlj1102 Feb 18, 2026
809883f
fix: replace Chinese “” to ""
jlj1102 Feb 18, 2026
70e38f1
fix: removed .Loc code
jlj1102 Feb 18, 2026
a18e556
fix: fixing tabs' names
jlj1102 Feb 18, 2026
51bec7f
feat: Localized More strings
jlj1102 Feb 19, 2026
a669be1
fix: A """
jlj1102 Feb 19, 2026
50f370b
fix: attempt to fix printout language #1
jlj1102 Feb 19, 2026
416b33b
Localization: Changed more string to localized strings
jlj1102 Feb 20, 2026
db6de65
Localization: Changed more string to localized strings #2
jlj1102 Feb 20, 2026
997d2ac
fix: 修正 NSLocalizedString 的拼写错误
jlj1102 Feb 20, 2026
cb13ac9
Localization: 更新错误提示信息和添加Anisette服务器描述
jlj1102 Feb 21, 2026
bc95db4
Localization: 修改Anisette服务器描述中的空格格式
jlj1102 Feb 21, 2026
113dd5e
Localization: 添加中文README文件并更新英文部分
jlj1102 Feb 21, 2026
12c6288
Update README.md
jlj1102 Feb 21, 2026
2e7a026
Update README_CN.md
jlj1102 Feb 21, 2026
1512f2b
Merge pull request #1 from jlj1102/multilang
jlj1102 Feb 21, 2026
5517434
Fix: fixed urls in README
jlj1102 Feb 21, 2026
50c1cb1
Fix: added hud name
jlj1102 Feb 21, 2026
431a919
Fix: <br>
jlj1102 Feb 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.2'
xcode-version: '26.3'
- name: Checkout
uses: actions/checkout@v3
with:
Expand Down Expand Up @@ -63,13 +63,13 @@ jobs:
scheme: ${{ steps.scheme.outputs.scheme }}
filetype_parameter: ${{ steps.filetype_parameter.outputs.filetype_parameter }}
file_to_build: ${{ steps.file_to_build.outputs.file_to_build }}
run: xcodebuild analyze -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -sdk iphoneos -arch arm64 -configuration Release CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]}
run: xcodebuild analyze -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "generic/platform=iOS" -sdk iphoneos -configuration Release CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]}
- name: Archive
env:
scheme: ${{ steps.scheme.outputs.scheme }}
filetype_parameter: ${{ steps.filetype_parameter.outputs.filetype_parameter }}
file_to_build: ${{ steps.file_to_build.outputs.file_to_build }}
run: xcodebuild archive -archivePath "$archive_path" -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -sdk iphoneos -arch arm64 -configuration Release CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]}
run: xcodebuild archive -archivePath "$archive_path" -scheme "$scheme" -destination "generic/platform=iOS" -"$filetype_parameter" "$file_to_build" -sdk iphoneos -configuration Release CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]}
- name: Tar Build Artifact
run: tar -cvf "$archive_path.xcarchive.tar" "$archive_path.xcarchive"
- name: Upload a Build Artifact
Expand Down Expand Up @@ -118,6 +118,7 @@ jobs:
with:
name: ${{ env.scheme }}.ipa
path: ${{ env.scheme }}.ipa
compression-level: 0

release__nightly:
name: Nightly Release
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
xcuserdata
xcuserdata
.vscode
10 changes: 10 additions & 0 deletions Entitlement.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
17EE68B02D848EA100B77523 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 17EE68AF2D848EA100B77523 /* Starscream */; };
17EE68B32D848EF000B77523 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 17EE68B22D848EF000B77523 /* KeychainAccess */; };
4EE1A6092D954CE9007D655E /* StosSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE1A6082D954CE9007D655E /* StosSign */; };
EN_LPROJ_BUILD /* en.lproj in Resources */ = {isa = PBXBuildFile; fileRef = EN_LPROJ /* en.lproj */; };
ZH_LPROJ_BUILD /* zh-Hans.lproj in Resources */ = {isa = PBXBuildFile; fileRef = ZH_LPROJ /* zh-Hans.lproj */; };

/* End PBXBuildFile section */

/* Begin PBXFileReference section */
173496F02D847E82007994DB /* Entitlement.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Entitlement.app; sourceTree = BUILT_PRODUCTS_DIR; };
17D124782D85856000796C1D /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = OpenSSL/Frameworks/OpenSSL.xcframework; sourceTree = "<group>"; };
EN_LPROJ /* en.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder.localized; path = Entitlement/Resources/en.lproj; sourceTree = "<group>"; };
ZH_LPROJ /* zh-Hans.lproj */ = {isa = PBXFileReference; lastKnownFileType = folder.localized; path = Entitlement/Resources/zh-Hans.lproj; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
Expand Down Expand Up @@ -58,6 +63,8 @@
173496F22D847E82007994DB /* Entitlement */,
173497042D847F5A007994DB /* Frameworks */,
173496F12D847E82007994DB /* Products */,
EN_LPROJ /* en.lproj */,
ZH_LPROJ /* zh-Hans.lproj */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -125,6 +132,7 @@
hasScannedForEncodings = 0;
knownRegions = (
en,
zh-Hans,
Base,
);
mainGroup = 173496E72D847E82007994DB;
Expand All @@ -149,6 +157,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EN_LPROJ_BUILD /* en.lproj in Resources */,
ZH_LPROJ_BUILD /* zh-Hans.lproj in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
25 changes: 13 additions & 12 deletions Entitlement/AnisetteDataHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// AltStore
//
// Created by Riley Testut on 1/7/20.
// Translated by jlj1102 on 2026/2/30.
// Copyright © 2020 Riley Testut. All rights reserved.
//

Expand Down Expand Up @@ -37,10 +38,10 @@ final class AnisetteDataHelper: WebSocketDelegate
{

if url == nil {
throw "No Anisette Server Found!"
throw (NSLocalizedString("noanisetteurl", comment: ""))
}

self.printOut("Anisette URL: \(self.url!.absoluteString)")
self.printOut("\(NSLocalizedString("anisetteurl", comment: "")) \(self.url!.absoluteString)")

let ans : AnisetteData
if let identifier = Keychain.shared.identifier,
Expand All @@ -67,7 +68,7 @@ final class AnisetteDataHelper: WebSocketDelegate
self.printOut("Error message contains -45061 (not provisioned), resetting adi.pb and retrying")
Keychain.shared.adiPb = nil
return try await provision()
} else { throw message ?? "Unknown error" }
} else { throw message ?? NSLocalizedString("unkerr", comment: "") }
}
}

Expand Down Expand Up @@ -114,21 +115,21 @@ final class AnisetteDataHelper: WebSocketDelegate
let jsonData = try JSONEncoder().encode(formattedJSON)
let anisette = try JSONDecoder().decode(AnisetteData.self, from: jsonData)

self.printOut("Anisette is valid!")
self.printOut("\(NSLocalizedString("validanisetteurl", comment: ""))")
return anisette
} catch {
self.printOut("Anisette is invalid!!!!")
self.printOut("\(NSLocalizedString("invalidanisetteurl", comment: ""))")
if v3 {
throw "Invalid anisette (the returned data may not have all the required fields)"
throw NSLocalizedString("invalidanisettefields", comment: "")
} else {
throw "Invalid anisette (the returned data may not have all the required fields)"
throw NSLocalizedString("invalidanisettefields", comment: "")
}
}
} else {
if v3 {
throw "Invalid anisette (the returned data may not be in JSON)"
throw NSLocalizedString("invalidanisettejson", comment: "")
} else {
throw "Invalid anisette (the returned data may not be in JSON)"
throw NSLocalizedString("invalidanisettejson", comment: "")
}
}
}
Expand Down Expand Up @@ -156,7 +157,7 @@ final class AnisetteDataHelper: WebSocketDelegate
return try await self.startProvisioningSession()
} else {
self.printOut("Apple didn't give valid URLs! Got response: \(String(data: data, encoding: .utf8) ?? "not utf8")")
throw "Apple didn't give valid URLs. Please try again later"
throw NSLocalizedString("novalidurlsgiven", comment: "")
}


Expand Down Expand Up @@ -356,8 +357,8 @@ final class AnisetteDataHelper: WebSocketDelegate
self.printOut("X-Mme-Device-Id: \(self.deviceId!)")

return
} else { throw "v1 server is not supported" }
} else { throw "Couldn't fetch client info. The returned data may not be in JSON" }
} else { throw NSLocalizedString("invalidv1server", comment: "") }
} else { throw NSLocalizedString("invalidclientinfo", comment: "") }
}

}
Expand Down
17 changes: 9 additions & 8 deletions Entitlement/AppIDView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Entitlement
//
// Created by s s on 2025/3/15.
// Modified by jlj1102 on 2026/2/19.
//
import SwiftUI

Expand All @@ -18,19 +19,19 @@ struct AppIDEditView : View {
Button {
Task { await addIncreasedMemoryLimit() }
} label: {
Text("Add Increased Memory Limit")
Text("addmemlimit")
}
}

Section {
Text(viewModel.result)
.font(.system(.subheadline, design: .monospaced))
} header: {
Text("Server Response")
Text("srvresp")
}
}
.alert("Error", isPresented: $errorShow){
Button("OK".loc, action: {
.alert("err", isPresented: $errorShow){
Button("ok".loc, action: {
})
} message: {
Text(errorInfo)
Expand Down Expand Up @@ -69,17 +70,17 @@ struct AppIDView : View {
}
}
} header: {
Text("App IDs")
Text("appids")
}

Section {
Button("Refresh") {
Button("refresh") {
Task { await refreshButtonClicked() }
}
}
}
.alert("Error", isPresented: $errorShow){
Button("OK".loc, action: {
.alert("err", isPresented: $errorShow){
Button("ok".loc, action: {
})
} message: {
Text(errorInfo)
Expand Down
6 changes: 3 additions & 3 deletions Entitlement/AppIDViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class AppIDModel : ObservableObject, Hashable {

func addIncreasedMemory() async throws {
guard let team = DataManager.shared.model.team, let session = DataManager.shared.model.session else {
throw "Please Login First"
throw NSLocalizedString("loginfirst", comment: "")
}

let dateFormatter = ISO8601DateFormatter()
Expand Down Expand Up @@ -71,7 +71,7 @@ class AppIDViewModel : ObservableObject {

func fetchAppIDs() async throws {
guard let team = DataManager.shared.model.team, let session = DataManager.shared.model.session else {
throw "Please Login First"
throw NSLocalizedString("loginfirst", comment: "")
}

let ids = try await withUnsafeThrowingContinuation { (c: UnsafeContinuation<[AppID], Error>) in
Expand All @@ -80,7 +80,7 @@ class AppIDViewModel : ObservableObject {
c.resume(throwing: error)
}
guard let appIDs else {
c.resume(throwing: "AppIDs is nil. Please try again or reopen the app.")
c.resume(throwing: NSLocalizedString("nulappid", comment: ""))
return
}
c.resume(returning: appIDs)
Expand Down
9 changes: 6 additions & 3 deletions Entitlement/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
"size" : "1024x1024",
"filename" : "icon.png"
},
{
"appearances" : [
Expand All @@ -14,7 +15,8 @@
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
"size" : "1024x1024",
"filename" : "transicon.png"
},
{
"appearances" : [
Expand All @@ -25,7 +27,8 @@
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
"size" : "1024x1024",
"filename" : "transicon.png"
}
],
"info" : {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Entitlement/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ struct ContentView: View {
TabView {
AppIDView(viewModel: AppIDViewModel())
.tabItem {
Label("App IDs".loc, systemImage: "square.stack.3d.up.fill")
Label("appids", systemImage: "square.stack.3d.up.fill")
}
SettingsView(viewModel: LoginViewModel())
.tabItem {
Label("Settings".loc, systemImage: "gearshape.fill")
Label("settings", systemImage: "gearshape.fill")
}
}

Expand Down
8 changes: 4 additions & 4 deletions Entitlement/LoginViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ class LoginViewModel: ObservableObject {
if let account, let session {
c.resume(returning: (account, session))
} else {
c.resume(throwing: "Account or session is nil. Please try again or reopen the app.")
c.resume(throwing: NSLocalizedString("nilaccountsession", comment: ""))
}
}
}
logging(text: "Successfully signed in")
logging(text: NSLocalizedString("succeedlogin", comment: ""))

DataManager.shared.model.account = account
DataManager.shared.model.session = session
Keychain.shared.appleIDEmailAddress = self.appleID
Keychain.shared.appleIDPassword = self.password

let team = try await fetchTeam(for: account, session: session)
logging(text: "Successfully fetched team")
logging(text: NSLocalizedString("succeedteamfetch", comment: ""))
DataManager.shared.model.team = team

Task{ await MainActor.run {
Expand All @@ -104,7 +104,7 @@ class LoginViewModel: ObservableObject {
}
}
guard let fetchedTeams, !fetchedTeams.isEmpty, let team = fetchedTeams.first else {
throw "Unable to Fetch Team!"
throw NSLocalizedString("failfetchteam", comment: "")
}

return team
Expand Down
40 changes: 40 additions & 0 deletions Entitlement/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// From AppIDView.swift
appids = "App IDs";
addmemlimit = "Add Increased Memory Limit";
srvresp = "Server Response";
err = "ERROR";
ok = "OK";
refresh = "Refresh";
loginfirst = "Please Login first (OR your login status is bad)";
// From AnisetteDataHelper.swift
noanisetteurl = "No Anisette Server is found. (https://ani.sidestore.io By Default)";
novalidurlsgiven = "Apple didn't give valid URLs. Please try again later";
anisetteurl = "Anisette URL:";
nilappid = "AppIDs is nil. Please try again or reopen the app.";
unkerr = "Unknown Error";
validanisetteurl = "Anisette URL valid!";
nilaccountsession = "Account or session is nil. Please try again or reopen the app.";
succeedlogin = "Logged in successfully!";
invalidanisetteurl = "Anisette URL is not valid!!!";
invalidanisettejson = "Invalid anisette (the returned data may not be in JSON)";
invalidanisettefields = "Invalid anisette (the returned data may not have all the required fields)";
invalidv1server = "Invalid server";
invalidclientinfo = "Couldn't fetch client info. The returned data may not be in JSON";
// From SettingsView.swift & LoginViewModel.swift
settings = "Settings";
email = "Email";
teamid = "Team ID";
login = "Login";
account = "Account";
aniserv = "Anisette Server";
clrkey = "Clear Keychain";
clrkeydesc = "If something went wrong during signing in, please try to clean up the keychain, repoen the app and try again.";
password = "Password";
twofa = "Verification Code";
continue = "Continue";
debugging = "Debugging";
cancel = "Cancel";
succeedteamfetch = "Successfully fetched team";
failfetchteam = "Unable to fetch team!";
aniservdesc = "Default is ( https://ani.sidestore.io ), you may need to restart the app for it to take effect.";

39 changes: 39 additions & 0 deletions Entitlement/Resources/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// From AppIDView.swift
appids = "应用ID";
addmemlimit = "增加内存上限";
srvresp = "服务器响应";
err = "错误";
ok = "确定";
refresh = "刷新";
loginfirst = "请你先登录喵~(如果已经登录就是出错了)";
// From AnisetteDataHelper.swift
noanisetteurl = "找不到Anisette服务器喵,默认是(https://ani.sidestore.io)喵~";
novalidurlsgiven = "苹果没有给出有效的URL喵~,请稍后再试喵~";
anisetteurl = "Anisette服务器:";
nilappid = "appid是空的喵~,重新启动软件喵~";
unkerr = "未知错误";
validanisetteurl = "Anisette服务器有效!";
nilaccountsession = "账号无效喵~";
succeedlogin = "登陆成功!恭喜!";
invalidanisetteurl = "这个Anesette服务器不能用喵~";
invalidanisettejson = "Anisette数据无效(返回的数据可能不是JSON格式)喵~";
invalidanisettefields = "Anisette数据无效(返回的数据可能没有所有必要的字段)喵~";
invalidv1server = "服务器无效";
invalidclientinfo = "无法获取客户端信息(返回的数据可能不是JSON格式)喵~";
// From SettingsView.swift & LoginViewModel.swift
settings = "设置";
email = "邮箱";
teamid = "团队ID";
login = "登录";
account = "账号";
aniserv = "Anisette服务器";
clrkey = "清理钥匙串";
clrkeydesc = "如果登录时报错,请尝试清理钥匙串,再重新打开软件。";
password = "密码";
twofa = "二次验证码";
continue = "继续";
debugging = "调试日志";
cancel = "取消";
succeedteamfetch = "团队获取成功";
failfetchteam = "无法获取团队!";
aniservdesc = "默认是( https://ani.sidestore.io ),可能需要重启以生效";
Loading