我有一个JSON文件,如下所示:
[
{
"id": 123,
"textEN": "something",
"textDE": "irgendwas"
},
...
]
我的环境中有一个结构体:
struct Something: Codable, Identifiable, Hashable {
var id: Int
var text: String
}
解析用户正在使用的语言的最好和/或最简单的方法是什么?我可以通过Locale.current.language.languageCode?.identifier
查看用户设置语言。但是我应该如何总是只解析特定的语言呢?我考虑了以下方法,但偶然发现了一些问题:
- 解析所有不同的语言并在视图中使用切换用例。可以工作,但在我看来是非常非常糟糕的代码。
- 解析所有不同的语言,并在视图之前实现另一个逻辑层。在数据被视图使用之前,管理器或视图模型或其他将负责删除除用户翻译之外的所有翻译。应该有效,但有没有更好的选择?
- 以某种方式只解析和存储当前正在使用的数据中的一种语言。可能是最好的解决方案,但我没能让这个工作。我试图实现一个CodingKey,这是一个连接字符串("text"+语言代码),但这是不可能的,或者至少我不能弄清楚如何,使用一个变量作为CodingKey。
您可以尝试这种方法,使用动态密钥AnyKey
(如评论和链接中提到的),一个init(from decoder: Decoder)
和一个langKey
只读取你想要的语言。
示例代码展示了一种非常简单的方法使用class LingoModel: ObservableObject
读取json数据。根据自己的目的调整代码。
import Foundation
import SwiftUI
class LingoModel: ObservableObject {
@Published var langs = [Lingo]()
static var langKey = "textEN"
func fetchData(for lang: String) {
LingoModel.langKey = lang
let json = """
[
{
"id": 123,
"textEN": "something",
"textDE": "irgendwas"
},
{
"id": 124,
"textEN": "something2",
"textDE": "irgendwas2"
},
{
"id": 125,
"textEN": "something2",
"textDE": "irgendwas2",
"textJA": "日本語"
}
]
"""
do {
// simulated data from an API
let data = Data(json.utf8)
langs = try JSONDecoder().decode([Lingo].self, from: data)
} catch {
print(error)
}
}
}
struct ContentView: View {
@StateObject var model = LingoModel()
@State var selected: String = "textEN"
var body: some View {
VStack {
Picker("", selection: $selected) {
Text("textEN").tag("textEN")
Text("textDE").tag("textDE")
Text("textJA").tag("textJA")
}.pickerStyle(.segmented).frame(width: 222)
.onChange(of: selected) { lang in
model.fetchData(for: lang)
}
List(model.langs) { lingo in
Text(lingo.text)
}
}
.onAppear {
model.fetchData(for: selected)
}
}
}
struct Lingo: Codable, Identifiable, Hashable {
var id: Int
var text: String
struct AnyKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) { self.stringValue = stringValue }
init?(intValue: Int) { return nil } // not used
}
init(from decoder: Decoder) throws {
let container1 = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container1.decode(Int.self, forKey: .id)
self.text = ""
let container2 = try decoder.container(keyedBy: AnyKey.self)
if let theKey = container2.allKeys.first(where: {$0.stringValue == LingoModel.langKey}),
let txt = try? container2.decode(String.self, forKey: theKey) {
self.text = txt
}
}
}
要只解析用户正在使用的语言,您可以使用可解码结构体的init(from decoder: Decoder)
方法来选择性地只解码用户设置的语言。
下面是一个示例实现:
let json = """
{
"id": 123,
"textEN": "something",
"textDE": "irgendwas"
}
""".data(using: .utf8)!
struct Something: Decodable, Identifiable, Hashable {
var id: Int
var text: String
enum CodingKeys: String, CodingKey {
case id, textEN, textDE
}
init(from decoder: Decoder) throws {
// change language value with `Locale.current.languageCode?.identifier`
let language = "en"
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
let textKeyForLanguage = language == "en" ? CodingKeys.textEN : CodingKeys.textDE
self.text = try container.decode(String.self, forKey: textKeyForLanguage)
}
}
在这个实现中,您使用CodingKeys枚举来定义JSON文件中属性的键。然后,在init(from decoder: Decoder)
方法中,您首先获得用户选择的语言,然后使用它选择性地只解码与所选语言对应的属性。
您可以将默认的language
值替换为Locale.current.language.languageCode?.identifier
以获得用户选择的语言,但请确保处理未设置或未识别用户语言的情况。
请注意,我符合
Decodable
而不是Codable
,所以如果你需要它是Encodable
,你也必须实现func encode(to encoder: Encoder) throws
。
希望这对你有帮助!