我为我的 iOS 框架项目定义了一个字符串目录,而不是 iOS 应用程序项目。如果出现问题,它似乎不会执行任何翻译,也不会提供错误反馈。该项目当前使用应用程序级本地化,而不是使用系统的本地化,因此我必须维护
UserDefaults
以保留上次使用的区域设置标识符。
首先,这里有一个我定义为
ObservableObject
的辅助类,以帮助进行区域设置切换,还包括 Locale
类的扩展,以提供使用 String
到 Locale
init 的快捷方式。这里的示例用于测试英语和阿拉伯语翻译。
class LanguageSettings: ObservableObject {
init() {
var localeId = UserDefaults.standard.value(forKey: "locale") as? String
if localeId == nil {
localeId = Locale.current.identifier
}
switch localeId?.lowercased() {
case "en","en-us":
break
case "ar":
locale = LanguageSettings.arabic
break
default:
break
}
}
init(localeTest: Locale) {
locale = localeTest
}
@Published var locale: Locale = Locale(identifier: "EN-US") {
didSet {
switch locale.identifier.lowercased() {
case "en", "en-us":
break
case "ar":
break
default:
locale = oldValue
break
}
updateUserDefaults()
}
}
private func updateUserDefaults() {
UserDefaults.standard.setValue(locale.identifier, forKey: "locale")
}
static let english = Locale(identifier: "EN-US")
static let arabic = Locale(identifier: "AR")
static let availableLocales = [
english, arabic
]
}
extension Locale {
func string(localized: String.LocalizationValue, comment: StaticString? = nil) -> String {
let localizedString = String(localized: localized, bundle: Bundle.main, locale: self, comment: comment)
print("Value for \(localized) for \(identifier) is \(localizedString)")
return localizedString
}
}
然后这是一个使用它的 SwiftUI 视图。
import Foundation
import SwiftUI
struct ErrorDisplay: View {
@EnvironmentObject private var languageSettings: LanguageSettings
@Environment(\.locale) private var locale: Locale
var body: some View {
let localeIdToName: [String: String] = [
"en": "English",
"en-us": "English",
"ar": "Arabic"
]
// let locale = languageSettings.locale
GeometryReader { _ in
VStack {
Image(.exclamationTriangleSolid).foregroundColor(.white)
.padding(.top, 10)
Text(locale.identifier)
Divider().background(.white)
HStack {
Text(localeIdToName[locale.identifier.lowercased()] ?? "-")
.padding(.all, 5)
.foregroundColor(.black)
Image(systemName: "chevron.down").foregroundColor(.black)
.padding(.trailing, 5)
}
.background(.white)
.padding(.all, 8)
.containerShape(Rectangle())
.onTapGesture {
withAnimation {
showLocalePicker = true
}
}
Text(message ?? "An error has occurred")
.multilineTextAlignment(.center)
.font(.system(size: 16))
.foregroundColor(.white)
Button(
action: onReload
) {
Text(locale.string(localized: "Reload Video")
}
.padding(.vertical, 10)
}
}
.sheet(isPresented: $showLocalePicker) {
Text(
locale.string(localized: "Select Locale")
).padding()
Text("Select Locale")
ForEach(0..<availableLocales.count) { i in
let locale = availableLocales[i]
let name = localeIdToName[locale.identifier.lowercased()]
Button(
action: {
withAnimation {
showLocalePicker = false
}
if name != nil {
languageSettings.locale = locale
}
}
) {
Text(name ?? "-")
}.padding()
}
Button(
role: .destructive,
action: {
withAnimation {
showLocalePicker = false
}
}
) {
Text(locale.string(localized: "Cancel"))
}.padding()
}
}
var message: String? = nil
var availableLocales: [Locale] = LanguageSettings.availableLocales
var onReload: () -> Void
@State private var showLocalePicker = false
}
我也尝试过像这样预览视图。
#Preview {
ErrorDisplay(
onReload: {}
)
.environment(\.locale, LanguageSettings.arabic
)
.environmentObject(
LanguageSettings(localeTest: LanguageSettings.arabic)
)
}
然后我确认注入的
@Environment
水平Locale
确实是正确的。我还定义了 Locale
类的扩展来帮助跟踪。
每次我更改区域设置时,它都会触发
@Environment
区域设置来重建视图,因此每次我更改区域设置时它都会继续打印。但打印结果表明它总是返回本地化密钥字符串。
我看过类似的帖子here,但尚未提供答案。
还要澄清的是,这是一个框架项目,我打算以
.xcframework
格式使用、构建和分发。我在这里不使用 Swift Package Manager 也不使用 Podfile。
在经过一些尝试和错误并阅读过去的帖子后回答我自己的问题,我用旧字符串文件替换了字符串目录,并生成了
.lproj
文件,参考了这个旧的答案,在从特定的Apple推荐后论坛帖子.
因此编辑我现有的代码
// Before
extension Locale {
func string(localized: String.LocalizationValue, comment: StaticString? = nil) -> String {
return String(localized: localized, bundle: bundle, locale: self, comment: comment)
}
}
进入这个
// After
extension Locale {
func string(localized: String, comment: String = "") -> String {
// Fetch my custom framework's bundle, defaults to Bundle.main
var bundle = Bundle(identifier: "com.framework.personal") ?? Bundle.main
// Look for lproj file path inside the bundle then load the localized bundle
if let bundlePath = bundle.path(forResource: languageCodeFromIdentifier, ofType: "lproj") {
bundle = Bundle(path: bundlePath) ?? bundle
}
return NSLocalizedString(localized, bundle: bundle, value: localized, comment: comment)
}
// Helper getter to fetch language code from the Locale as reading from language?.languageCode is not reliable after some tests.
var languageCodeFromIdentifier: String {
get {
let languageCode = identifier.split(separator: "_").first?.lowercased()
return languageCode ?? "en"
}
}
}
这对于我的用例来说现在很有效,其中字符串必须由应用程序定义的区域设置而不是
Locale.current
进行本地化。
这应该是一个快速的解决方法,但还不够干净,我将在我自己的项目中进行优化。