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]? + +}