Skip to content

Commit e825879

Browse files
committed
Update WordPressComLanguageDatabase
1 parent a252e96 commit e825879

File tree

10 files changed

+138
-185
lines changed

10 files changed

+138
-185
lines changed

Modules/Sources/WordPressKit/WordPressComServiceRemote+SiteCreation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public struct SiteCreationRequest: Encodable {
1111
public let tagline: String?
1212
public let siteURLString: String
1313
public let isPublic: Bool
14-
public let languageIdentifier: String
14+
public let languageIdentifier: Int
1515
public let shouldValidate: Bool
1616
public let clientIdentifier: String
1717
public let clientSecret: String
@@ -27,7 +27,7 @@ public struct SiteCreationRequest: Encodable {
2727
tagline: String?,
2828
siteURLString: String,
2929
isPublic: Bool,
30-
languageIdentifier: String,
30+
languageIdentifier: Int,
3131
shouldValidate: Bool,
3232
clientIdentifier: String,
3333
clientSecret: String,

Modules/Sources/WordPressShared/Utility/Languages.swift

Lines changed: 92 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,52 @@ import Foundation
22

33
/// This helper class allows us to map WordPress.com LanguageID's into human readable language strings.
44
///
5-
public class WordPressComLanguageDatabase: NSObject {
5+
public struct WordPressComLanguageDatabase {
6+
7+
public static let shared = WordPressComLanguageDatabase()
8+
69
// MARK: - Public Properties
710

811
/// Languages considered 'popular'
912
///
10-
public let popular: [Language]
13+
public let popular: [WPComLanguage]
1114

1215
/// Every supported language
1316
///
14-
public let all: [Language]
17+
public let all: [WPComLanguage]
1518

16-
/// Returns both, Popular and All languages, grouped
17-
///
18-
public let grouped: [[Language]]
19+
// /// Returns both, Popular and All languages, grouped
20+
// ///
21+
// public let grouped: [[WPComLanguage]]
22+
23+
/// Allow mocking the device language code for testing purposes
24+
private let _deviceLanguageCode: String?
1925

2026
// MARK: - Public Methods
2127

2228
/// Designated Initializer: will load the languages contained within the `Languages.json` file.
2329
///
24-
public override init() {
30+
private init() {
31+
// Parse the json file
32+
let path = Bundle.wordPressSharedBundle.path(forResource: "Languages", ofType: "json")
33+
let data = try! Data(contentsOf: URL(fileURLWithPath: path!))
34+
let bundle = try! JSONDecoder().decode(WPComLanguageBundle.self, from: data)
35+
36+
self.popular = bundle.popular
37+
self.all = bundle.all
38+
self._deviceLanguageCode = nil
39+
}
40+
41+
/// Specifically marked internal for used by test code
42+
internal init(deviceLanguageCode: String) {
2543
// Parse the json file
26-
let path = Bundle.wordPressSharedBundle.path(forResource: filename, ofType: "json")
27-
let raw = try! Data(contentsOf: URL(fileURLWithPath: path!))
28-
let parsed = try! JSONSerialization.jsonObject(with: raw, options: [.mutableContainers, .mutableLeaves]) as? NSDictionary
29-
30-
// Parse All + Popular: All doesn't contain Popular. Otherwise the json would have dupe data. Right?
31-
let parsedAll = Language.fromArray(parsed![Keys.all] as! [[String: Any]])
32-
let parsedPopular = Language.fromArray(parsed![Keys.popular] as! [[String: Any]])
33-
let merged = parsedAll + parsedPopular
34-
35-
// Done!
36-
popular = parsedPopular
37-
all = merged.sorted { $0.name < $1.name }
38-
grouped = [popular] + [all]
44+
let path = Bundle.wordPressSharedBundle.path(forResource: "Languages", ofType: "json")
45+
let data = try! Data(contentsOf: URL(fileURLWithPath: path!))
46+
let bundle = try! JSONDecoder().decode(WPComLanguageBundle.self, from: data)
47+
48+
self.popular = bundle.popular
49+
self.all = bundle.all
50+
self._deviceLanguageCode = deviceLanguageCode.lowercased()
3951
}
4052

4153
/// Returns the Human Readable name for a given Language Identifier
@@ -44,7 +56,7 @@ public class WordPressComLanguageDatabase: NSObject {
4456
///
4557
/// - Returns: A string containing the language name, or an empty string, in case it wasn't found.
4658
///
47-
@objc public func nameForLanguageWithId(_ languageId: Int) -> String {
59+
public func nameForLanguageWithId(_ languageId: Int) -> String {
4860
return find(id: languageId)?.name ?? ""
4961
}
5062

@@ -54,34 +66,14 @@ public class WordPressComLanguageDatabase: NSObject {
5466
///
5567
/// - Returns: The language with the matching Identifier, or nil, in case it wasn't found.
5668
///
57-
public func find(id: Int) -> Language? {
69+
public func find(id: Int) -> WPComLanguage? {
5870
return all.first(where: { $0.id == id })
5971
}
6072

61-
/// Returns the current device language as the corresponding WordPress.com language ID.
62-
/// If the language is not supported, it returns 1 (English).
63-
///
64-
/// This is a wrapper for Objective-C, Swift code should use deviceLanguage directly.
65-
///
66-
@objc(deviceLanguageId)
67-
public func deviceLanguageIdNumber() -> NSNumber {
68-
return NSNumber(value: deviceLanguage.id)
69-
}
70-
71-
/// Returns the slug string for the current device language.
72-
/// If the language is not supported, it returns "en" (English).
73-
///
74-
/// This is a wrapper for Objective-C, Swift code should use deviceLanguage directly.
75-
///
76-
@objc(deviceLanguageSlug)
77-
public func deviceLanguageSlugString() -> String {
78-
return deviceLanguage.slug
79-
}
80-
8173
/// Returns the current device language as the corresponding WordPress.com language.
8274
/// If the language is not supported, it returns English.
8375
///
84-
public var deviceLanguage: Language {
76+
public var deviceLanguage: WPComLanguage {
8577
let variants = LanguageTagVariants(string: deviceLanguageCode)
8678
for variant in variants {
8779
if let match = self.languageWithSlug(variant) {
@@ -93,101 +85,36 @@ public class WordPressComLanguageDatabase: NSObject {
9385

9486
/// Searches for a WordPress.com language that matches a language tag.
9587
///
96-
fileprivate func languageWithSlug(_ slug: String) -> Language? {
88+
fileprivate func languageWithSlug(_ slug: String) -> WPComLanguage? {
9789
let search = languageCodeReplacements[slug] ?? slug
98-
99-
// Use lazy evaluation so we stop filtering as soon as we got the first match
100-
return all.lazy.filter({ $0.slug == search }).first
101-
}
102-
103-
/// Overrides the device language. For testing purposes only.
104-
///
105-
@objc func _overrideDeviceLanguageCode(_ code: String) {
106-
deviceLanguageCode = code.lowercased()
90+
return all.first { $0.slug == search }
10791
}
10892

109-
// MARK: - Public Nested Classes
93+
// MARK: - Private Variables
11094

111-
/// Represents a Language supported by WordPress.com
95+
/// The device's current preferred language, or English if there's no preferred language.
11296
///
113-
public class Language: Equatable {
114-
/// Language Unique Identifier
115-
///
116-
public let id: Int
117-
118-
/// Human readable Language name
119-
///
120-
public let name: String
97+
/// Specifically marked internal for used by test code
98+
internal var deviceLanguageCode: String {
12199

122-
/// Language's Slug String
123-
///
124-
public let slug: String
125-
126-
/// Localized description for the current language
127-
///
128-
public var description: String {
129-
return (Locale.current as NSLocale).displayName(forKey: NSLocale.Key.identifier, value: slug) ?? name
130-
}
131-
132-
/// Designated initializer. Will fail if any of the required properties is missing
133-
///
134-
init?(dict: [String: Any]) {
135-
guard let unwrappedId = (dict[Keys.identifier] as? NSNumber)?.intValue,
136-
let unwrappedSlug = dict[Keys.slug] as? String,
137-
let unwrappedName = dict[Keys.name] as? String else {
138-
id = Int.min
139-
name = String()
140-
slug = String()
141-
return nil
142-
}
143-
144-
id = unwrappedId
145-
name = unwrappedName
146-
slug = unwrappedSlug
100+
// Return the mocked language code, if set
101+
if let _deviceLanguageCode {
102+
return _deviceLanguageCode
147103
}
148104

149-
/// Given an array of raw languages, will return a parsed array.
150-
///
151-
public static func fromArray(_ array: [[String: Any]] ) -> [Language] {
152-
return array.compactMap {
153-
return Language(dict: $0)
154-
}
105+
guard let preferredLanguage = Locale.preferredLanguages.first else {
106+
return "en"
155107
}
156108

157-
public static func == (lhs: Language, rhs: Language) -> Bool {
158-
return lhs.id == rhs.id
159-
}
109+
return preferredLanguage.lowercased()
160110
}
161111

162-
// MARK: - Private Variables
163-
164-
/// The device's current preferred language, or English if there's no preferred language.
165-
///
166-
fileprivate lazy var deviceLanguageCode: String = {
167-
return NSLocale.preferredLanguages.first?.lowercased() ?? "en"
168-
}()
169-
170-
// MARK: - Private Constants
171-
fileprivate let filename = "Languages"
172-
173112
// (@koke 2016-04-29) I'm not sure how correct this mapping is, but it matches
174113
// what we do for the app translations, so they will at least be consistent
175114
fileprivate let languageCodeReplacements: [String: String] = [
176115
"zh-hans": "zh-cn",
177116
"zh-hant": "zh-tw"
178117
]
179-
180-
// MARK: - Private Nested Structures
181-
182-
/// Keys used to parse the raw languages.
183-
///
184-
fileprivate struct Keys {
185-
static let popular = "popular"
186-
static let all = "all"
187-
static let identifier = "i"
188-
static let slug = "s"
189-
static let name = "n"
190-
}
191118
}
192119

193120
/// Provides a sequence of language tags from the specified string, from more to less specific
@@ -210,3 +137,48 @@ private struct LanguageTagVariants: Sequence {
210137
}
211138
}
212139
}
140+
141+
// MARK: - Public Nested Classes
142+
143+
public struct WPComLanguageBundle: Codable {
144+
let popular: [WPComLanguage]
145+
let others: [WPComLanguage]
146+
147+
enum CodingKeys: String, CodingKey {
148+
case popular = "popular"
149+
case others = "all"
150+
}
151+
152+
var all: [WPComLanguage] {
153+
(popular + others).sorted { $0.name < $1.name }
154+
}
155+
}
156+
157+
/// Represents a Language supported by WordPress.com
158+
///
159+
public struct WPComLanguage: Codable, Equatable {
160+
161+
enum CodingKeys: String, CodingKey {
162+
case id = "i"
163+
case name = "n"
164+
case slug = "s"
165+
}
166+
167+
/// Language Unique Identifier
168+
///
169+
public let id: Int
170+
171+
/// Human readable Language name
172+
///
173+
public let name: String
174+
175+
/// Language's Slug String
176+
///
177+
public let slug: String
178+
179+
/// Localized description for the current language
180+
///
181+
public var description: String {
182+
return (Locale.current as NSLocale).displayName(forKey: NSLocale.Key.identifier, value: slug) ?? name
183+
}
184+
}

0 commit comments

Comments
 (0)