Xcode 字符串目录在 SwiftUI Framework (.xcframework) 项目中不起作用

问题描述 投票:0回答:1

我为我的 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
}

我的字符串目录已处于 100% 翻译进度,如下所示。

我也尝试过像这样预览视图。

#Preview {
    ErrorDisplay(
        onReload: {}
    )
        .environment(\.locale, LanguageSettings.arabic
        )
        .environmentObject(
            LanguageSettings(localeTest: LanguageSettings.arabic)
        )
}

然后我确认注入的

@Environment
水平
Locale
确实是正确的。我还定义了
Locale
类的扩展来帮助跟踪。

每次我更改区域设置时,它都会触发

@Environment
区域设置来重建视图,因此每次我更改区域设置时它都会继续打印。但打印结果表明它总是返回本地化密钥字符串。

我看过类似的帖子here,但尚未提供答案。

还要澄清的是,这是一个框架项目,我打算以

.xcframework
格式使用、构建和分发。我在这里不使用 Swift Package Manager 也不使用 Podfile

swift xcode swiftui localization string-catalog
1个回答
0
投票

在经过一些尝试和错误并阅读过去的帖子后回答我自己的问题,我用旧字符串文件替换了字符串目录,并生成了

.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
进行本地化。

这应该是一个快速的解决方法,但还不够干净,我将在我自己的项目中进行优化。

© www.soinside.com 2019 - 2024. All rights reserved.