diff --git a/MoviaBox.xcodeproj/xcshareddata/xcschemes/MoviaBox.xcscheme b/MoviaBox.xcodeproj/xcshareddata/xcschemes/MoviaBox.xcscheme
new file mode 100644
index 0000000..eb1d8c5
--- /dev/null
+++ b/MoviaBox.xcodeproj/xcshareddata/xcschemes/MoviaBox.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MoviaBox/AppDelegate/AppDelegate.swift b/MoviaBox/AppDelegate/AppDelegate.swift
index 472103b..7795583 100644
--- a/MoviaBox/AppDelegate/AppDelegate.swift
+++ b/MoviaBox/AppDelegate/AppDelegate.swift
@@ -29,7 +29,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// SPLoginManager.manager.requestVisitorLogin(completer: nil)
SPLoginManager.manager.updateUserInfo(completer: nil)
-
+ ///更新本地多语言
+ SPLocalizedManager.shared.updateLocalizedData(completer: nil)
///注册消息通知
registerAPNS()
diff --git a/MoviaBox/AppDelegate/SceneDelegate.swift b/MoviaBox/AppDelegate/SceneDelegate.swift
index 305ea5f..0f87d3b 100644
--- a/MoviaBox/AppDelegate/SceneDelegate.swift
+++ b/MoviaBox/AppDelegate/SceneDelegate.swift
@@ -15,6 +15,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
+ ///监听语言切换
+ NotificationCenter.default.addObserver(self, selector: #selector(localizedDidChange), name: SPLocalizedManager.localizedDidChange, object: nil)
+
let tabBarController = SPTabBarController()
SPAPPTool.mainTabBarController = tabBarController
@@ -93,4 +96,13 @@ extension SceneDelegate {
handleOpenAppMessage(webpageURL: nil)
}
+
+ ///语言切换
+ @objc private func localizedDidChange() {
+ let tabBarController = SPTabBarController()
+ SPAPPTool.mainTabBarController = tabBarController
+
+ window?.rootViewController = tabBarController
+ window?.makeKeyAndVisible()
+ }
}
diff --git a/MoviaBox/Base/Extension/UIColor+SPAdd.swift b/MoviaBox/Base/Extension/UIColor+SPAdd.swift
index aeed737..224c3b3 100644
--- a/MoviaBox/Base/Extension/UIColor+SPAdd.swift
+++ b/MoviaBox/Base/Extension/UIColor+SPAdd.swift
@@ -400,5 +400,9 @@ extension UIColor {
static func colorA8B8C3(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xA8B8C3, alpha: alpha)
}
+
+ static func color321F1F(alpha: CGFloat = 1) -> UIColor {
+ return color(hex: 0x321F1F, alpha: alpha)
+ }
}
diff --git a/MoviaBox/Base/Networking/API/SPSettingAPI.swift b/MoviaBox/Base/Networking/API/SPSettingAPI.swift
new file mode 100644
index 0000000..4eb90af
--- /dev/null
+++ b/MoviaBox/Base/Networking/API/SPSettingAPI.swift
@@ -0,0 +1,38 @@
+//
+// SPSettingAPI.swift
+// MoviaBox
+//
+// Created by 佳尔 on 2025/5/10.
+//
+
+import UIKit
+
+class SPSettingAPI: NSObject {
+
+ ///获取语言列表
+ static func requestLanguageList(completer: ((_ list: [SPLanguageModel]?) -> Void)?) {
+
+ var param = SPNetworkParameters(path: "/languges")
+ param.method = .get
+
+ SPNetwork.request(parameters: param) { (response: SPNetworkResponse>) in
+ completer?(response.data?.list)
+ }
+ }
+
+ ///获取语言数据
+ static func requestLocalizedData(key: String, completer: ((_ model: SPLocalizedModel?) -> Void)?) {
+ var param = SPNetworkParameters(path: "/translates")
+ param.method = .get
+ param.parameters = [
+ "lang_key" : key
+ ]
+
+ SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in
+ completer?(response.data)
+ }
+
+ }
+
+
+}
diff --git a/MoviaBox/Class/Mine/Controller/SPLanguageViewController.swift b/MoviaBox/Class/Mine/Controller/SPLanguageViewController.swift
new file mode 100644
index 0000000..ad1e995
--- /dev/null
+++ b/MoviaBox/Class/Mine/Controller/SPLanguageViewController.swift
@@ -0,0 +1,125 @@
+//
+// SPLanguageViewController.swift
+// MoviaBox
+//
+// Created by 佳尔 on 2025/5/10.
+//
+
+import UIKit
+
+class SPLanguageViewController: SPViewController {
+
+
+ private lazy var dataArr: [SPLanguageModel] = []
+
+ private lazy var currentLocalizedKey = SPLocalizedManager.shared.currentLocalizedKey
+
+ private lazy var tableView: SPTableView = {
+ let tableView = SPTableView(frame: .zero, style: .insetGrouped)
+ tableView.delegate = self
+ tableView.dataSource = self
+ tableView.sectionFooterHeight = 0
+ tableView.tableHeaderView = UIView()
+ tableView.rowHeight = 60
+ tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: kSPTabbarSafeBottomMargin + 10, right: 0)
+ SPLanguageCell.registerCell(tableView: tableView)
+ return tableView
+ }()
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.edgesForExtendedLayout = .top
+ self.title = "Language".localized
+
+ requestDataList()
+
+ _setupUI()
+
+// SPSettingAPI.requestLocalizedData()
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ self.navigationController?.setNavigationBarHidden(false, animated: true)
+ setNavigationNormalStyle(backgroundColor: .clear, isTranslucent: true)
+ }
+
+
+
+}
+
+extension SPLanguageViewController {
+
+ private func _setupUI() {
+ view.addSubview(tableView)
+
+ tableView.snp.makeConstraints { make in
+ make.left.right.bottom.equalToSuperview()
+ make.top.equalToSuperview().offset(kSPNavBarHeight)
+ }
+ }
+
+}
+
+//MARK: -------------- UITableViewDelegate & UITableViewDataSource --------------
+extension SPLanguageViewController: UITableViewDelegate, UITableViewDataSource {
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let model = dataArr[indexPath.section]
+ let cell = SPLanguageCell.dequeueReusableCell(tableView: tableView, indexPath: indexPath)
+ cell.model = model
+ cell.sp_isSelected = model.lang_key == currentLocalizedKey
+ return cell
+ }
+
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return 1
+ }
+
+ func numberOfSections(in tableView: UITableView) -> Int {
+ return self.dataArr.count
+ }
+
+ func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+ return 12
+ }
+
+ func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
+ return 0
+ }
+
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let model = dataArr[indexPath.section]
+ if model.lang_key == currentLocalizedKey { return }
+
+ if let key = model.lang_key {
+ SPHUD.show()
+ SPLocalizedManager.shared.updateLocalizedData(key: key) { (finish) in
+ if finish {
+ SPLocalizedManager.shared.currentLocalizedKey = key
+ NotificationCenter.default.post(name: SPLocalizedManager.localizedDidChange, object: nil)
+ }
+ SPHUD.dismiss()
+ }
+ }
+
+
+ }
+
+}
+
+extension SPLanguageViewController {
+
+ private func requestDataList() {
+ SPSettingAPI.requestLanguageList { [weak self] list in
+ guard let self = self else { return }
+
+ if let list = list {
+ self.dataArr = list
+ self.tableView.reloadData()
+ }
+ }
+
+ }
+
+}
diff --git a/MoviaBox/Class/Mine/Controller/SPMineViewController.swift b/MoviaBox/Class/Mine/Controller/SPMineViewController.swift
index 5fe57ec..a061bd6 100644
--- a/MoviaBox/Class/Mine/Controller/SPMineViewController.swift
+++ b/MoviaBox/Class/Mine/Controller/SPMineViewController.swift
@@ -11,7 +11,7 @@ class SPMineViewController: SPViewController {
private lazy var dataArr: [SPMineItem] = {
let arr = [
-// SPMineItem(type: .language, iconImage: UIImage(named: "language_icon_01"), title: "Language".localized),
+ SPMineItem(type: .language, iconImage: UIImage(named: "language_icon_01"), title: "Language".localized),
SPMineItem(type: .feedBack, iconImage: UIImage(named: "feed_back_icon_01"), title: "FeedBack".localized),
SPMineItem(type: .privacyPolicy, iconImage: UIImage(named: "privacy_policy_icon_01"), title: "Privacy Policy".localized),
SPMineItem(type: .userAgreement, iconImage: UIImage(named: "user_agreement_icon_01"), title: "User Agreement".localized),
@@ -138,6 +138,10 @@ extension SPMineViewController: UITableViewDelegate, UITableViewDataSource {
let vc = SPSettingsViewController()
self.navigationController?.pushViewController(vc, animated: true)
+ case .language:
+ let vc = SPLanguageViewController()
+ self.navigationController?.pushViewController(vc, animated: true)
+
default:
break
diff --git a/MoviaBox/Class/Mine/Model/SPLanguageModel.swift b/MoviaBox/Class/Mine/Model/SPLanguageModel.swift
new file mode 100644
index 0000000..b407e38
--- /dev/null
+++ b/MoviaBox/Class/Mine/Model/SPLanguageModel.swift
@@ -0,0 +1,22 @@
+//
+// SPLanguageModel.swift
+// MoviaBox
+//
+// Created by 佳尔 on 2025/5/10.
+//
+
+import UIKit
+import SmartCodable
+
+class SPLanguageModel: SPModel, SmartCodable {
+
+ var cn_name: String?
+ var show_name: String?
+ var id: String?
+ var is_up_to_list: String?
+ var lang_key: String?
+// var description: String?
+ ///1:当前语言
+ var is_default: Int?
+
+}
diff --git a/MoviaBox/Class/Mine/View/SPLanguageCell.swift b/MoviaBox/Class/Mine/View/SPLanguageCell.swift
new file mode 100644
index 0000000..aa731c4
--- /dev/null
+++ b/MoviaBox/Class/Mine/View/SPLanguageCell.swift
@@ -0,0 +1,91 @@
+//
+// SPLanguageCell.swift
+// MoviaBox
+//
+// Created by 佳尔 on 2025/5/10.
+//
+
+import UIKit
+
+class SPLanguageCell: SPTableViewCell {
+
+ var model: SPLanguageModel? {
+ didSet {
+ titleLabel.text = model?.show_name
+
+ if model?.is_default == 1 {
+ self.contentView.layer.borderColor = UIColor.colorFF3232().cgColor
+ self.selectedButton.isSelected = true
+ } else {
+ self.contentView.layer.borderColor = UIColor.clear.cgColor
+ self.selectedButton.isSelected = false
+ }
+ }
+ }
+
+ var sp_isSelected = false {
+ didSet {
+ if sp_isSelected {
+ self.contentView.layer.borderColor = UIColor.colorFF3232().cgColor
+ self.selectedButton.isSelected = true
+ } else {
+ self.contentView.layer.borderColor = UIColor.clear.cgColor
+ self.selectedButton.isSelected = false
+ }
+ }
+ }
+
+ private lazy var titleLabel: UILabel = {
+ let label = UILabel()
+ label.font = .fontMedium(ofSize: 14)
+ label.textColor = .colorFFFFFF()
+ return label
+ }()
+
+ private lazy var selectedButton: UIButton = {
+ let button = UIButton(type: .custom)
+ button.isUserInteractionEnabled = false
+ button.setImage(UIImage(named: "check_icon_01"), for: .normal)
+ button.setImage(UIImage(named: "check_icon_01_selected"), for: .selected)
+ return button
+ }()
+
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
+
+ self.contentView.backgroundColor = .color321F1F()
+ self.contentView.layer.borderWidth = 1
+ self.contentView.layer.cornerRadius = 10
+ self.contentView.layer.masksToBounds = true
+
+
+ _setupUI()
+ }
+
+ @MainActor required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+
+
+}
+
+extension SPLanguageCell {
+
+ private func _setupUI() {
+ contentView.addSubview(titleLabel)
+ contentView.addSubview(selectedButton)
+
+ titleLabel.snp.makeConstraints { make in
+ make.centerY.equalToSuperview()
+ make.left.equalToSuperview().offset(16)
+ }
+
+ selectedButton.snp.makeConstraints { make in
+ make.centerY.equalToSuperview()
+ make.right.equalToSuperview().offset(-16)
+ }
+
+ }
+
+}
diff --git a/MoviaBox/Libs/SPLocalizedManager/SPLocalizedManager.swift b/MoviaBox/Libs/SPLocalizedManager/SPLocalizedManager.swift
index 8b41dbc..3470b3b 100644
--- a/MoviaBox/Libs/SPLocalizedManager/SPLocalizedManager.swift
+++ b/MoviaBox/Libs/SPLocalizedManager/SPLocalizedManager.swift
@@ -9,38 +9,55 @@ import UIKit
class SPLocalizedManager: NSObject {
static let shared = SPLocalizedManager()
+ private let LocalizedUserDefaultsKey = "SPLocalizedManager.LocalizedUserDefaultsKey"
+ private let LocalizedDataUserDefaultsKey = "SPLocalizedManager.LocalizedDataUserDefaultsKey"
- private let userDefaultsKey = "AppLocalized"
+
+ ///语言列表
+ var languageList: [SPLanguageModel]?
+
+ ///多语言数据
+ private lazy var localizedData: [String : String]? = UserDefaults.standard.object(forKey: LocalizedDataUserDefaultsKey) as? [String : String]
+ {
+ didSet {
+ UserDefaults.standard.set(localizedData, forKey: LocalizedDataUserDefaultsKey)
+ UserDefaults.standard.synchronize()
+ }
+ }
// 获取当前语言代码(如果用户未手动设置,则返回系统语言)
var currentLocalizedKey: String {
get {
// return UserDefaults.standard.string(forKey: userDefaultsKey) ?? Locale.preferredLanguages.first ?? "en"
- return "en"
+ return UserDefaults.standard.string(forKey: LocalizedUserDefaultsKey) ?? "en"
}
set {
- UserDefaults.standard.set(newValue, forKey: userDefaultsKey)
+ UserDefaults.standard.set(newValue, forKey: LocalizedUserDefaultsKey)
UserDefaults.standard.synchronize()
- NotificationCenter.default.post(name: SPLocalizedManager.localizedDidChange, object: nil)
}
}
// 判断是否跟随系统
var isFollowingSystem: Bool {
- return UserDefaults.standard.string(forKey: userDefaultsKey) == nil
+ return UserDefaults.standard.string(forKey: LocalizedUserDefaultsKey) == nil
}
// 还原为系统默认语言
func resetToSystemLanguage() {
- UserDefaults.standard.removeObject(forKey: userDefaultsKey)
+ UserDefaults.standard.removeObject(forKey: LocalizedUserDefaultsKey)
UserDefaults.standard.synchronize()
}
// 获取本地化字符串
func localizedString(forKey key: String, tableName: String? = nil) -> String {
- if let selectedLanguage = UserDefaults.standard.string(forKey: userDefaultsKey),
+ if let localizedData = localizedData,
+ let text = localizedData[key] {
+ return text
+
+ } else if let selectedLanguage = UserDefaults.standard.string(forKey: LocalizedUserDefaultsKey),
let bundlePath = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"),
let bundle = Bundle(path: bundlePath) {
+
return bundle.localizedString(forKey: key, value: nil, table: tableName)
} else {
return NSLocalizedString(key, tableName: tableName, bundle: .main, value: "", comment: "")
@@ -48,6 +65,31 @@ class SPLocalizedManager: NSObject {
}
}
+extension SPLocalizedManager {
+ ///获取本地化数据
+ func updateLocalizedData(key: String = SPLocalizedManager.shared.currentLocalizedKey, completer: ((_ finish: Bool) -> Void)?) {
+ SPSettingAPI.requestLocalizedData(key: key) { [weak self] model in
+ guard let self = self else { return }
+ guard let model = model else {
+ completer?(false)
+ return
+ }
+ if let languageList = model.languages, languageList.count > 0 {
+ self.languageList = languageList
+ }
+
+ if let localizedData = model.translates {
+ self.localizedData = localizedData
+ completer?(true)
+ } else {
+ completer?(false)
+ }
+ }
+ }
+}
+
+
+
extension SPLocalizedManager {
static let localizedDidChange = Notification.Name(rawValue: "SPLocalizedManager.localizedDidChange")
diff --git a/MoviaBox/Libs/SPLocalizedManager/SPLocalizedModel.swift b/MoviaBox/Libs/SPLocalizedManager/SPLocalizedModel.swift
new file mode 100644
index 0000000..cc45c7d
--- /dev/null
+++ b/MoviaBox/Libs/SPLocalizedManager/SPLocalizedModel.swift
@@ -0,0 +1,18 @@
+//
+// SPLocalizedModel.swift
+// MoviaBox
+//
+// Created by 佳尔 on 2025/5/10.
+//
+
+import UIKit
+import SmartCodable
+
+class SPLocalizedModel: SPModel, SmartCodable {
+
+ ///翻译数据
+ var translates: [String : String]?
+
+ var languages: [SPLanguageModel]?
+
+}