Skip to content

Commit 42a49ac

Browse files
committed
fix: prioritize plural localization over simple strings in LocalLocalizationExtractor and LocalizationProvider
1 parent e2c07c1 commit 42a49ac

4 files changed

Lines changed: 51 additions & 15 deletions

File tree

Sources/CrowdinSDK/CrowdinSDK/Localization/Extractor/LocalLocalizationExtractor.swift

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,18 @@ final class LocalLocalizationExtractor {
111111

112112
// Localization methods
113113
func localizedString(for key: String) -> String? {
114-
var string = self.localizationDict[key]
114+
// Check plurals first - if a key has plurals defined, those take precedence over simple strings
115+
// This fixes the issue where keys exist in both .strings and .stringsdict files
116+
var string = self.pluralsBundle?.bundle?.swizzled_LocalizedString(forKey: key, value: nil, table: nil)
117+
// Plurals localization works as default bundle localization. In case localized string for key is missing the key string will be returned.
118+
// To prevent issues with localization where key equals value(for example for english language) we need to set nil here.
119+
if string == key {
120+
string = nil
121+
}
122+
123+
// If no plural exists, fall back to simple strings
115124
if string == nil {
116-
string = self.pluralsBundle?.bundle?.swizzled_LocalizedString(forKey: key, value: nil, table: nil)
117-
// Plurals localization works as default bundle localization. In case localized string for key is missing the key string will be returned.
118-
// To prevent issues with localization where key equals value(for example for english language) we need to set nil here.
119-
if string == key {
120-
string = nil
121-
}
125+
string = self.localizationDict[key]
122126
}
123127
return string
124128
}

Sources/CrowdinSDK/CrowdinSDK/Localization/Provider/LocalizationProvider.swift

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,19 @@ class LocalizationProvider: NSObject, LocalizationProviderProtocol {
168168

169169
// Localization methods
170170
func localizedString(for key: String) -> String? {
171-
var string = self.strings[key]
171+
// Check plurals first - if a key has plurals defined, those take precedence over simple strings
172+
// This fixes the issue where keys exist in both .strings and .stringsdict files
173+
var string = self.pluralsBundle?.bundle?.swizzled_LocalizedString(forKey: key, value: nil, table: nil)
174+
// Plurals localization works as default bundle localization.
175+
// In case localized string for key is missing the key string will be returned.
176+
// To prevent issues with localization where key equals value(for example for english language) we need to set nil here.
177+
if string == key {
178+
string = nil
179+
}
180+
181+
// If no plural exists, fall back to simple strings
172182
if string == nil {
173-
string = self.pluralsBundle?.bundle?.swizzled_LocalizedString(forKey: key, value: nil, table: nil)
174-
// Plurals localization works as default bundle localization.
175-
// In case localized string for key is missing the key string will be returned.
176-
// To prevent issues with localization where key equals value(for example for english language) we need to set nil here.
177-
if string == key {
178-
string = nil
179-
}
183+
string = self.strings[key]
180184
}
181185
return string
182186
}

Tests/Tests/en.lproj/Localizable.strings

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@
1010
"test_key_with_string_parameter" = "test value with parameter - %@ [B]"; // B - buldle
1111
"test_key_with_int_parameter" = "test value with parameter - %lu [B]"; // B - buldle
1212
"test_key_with_two_parameters" = "test value with parameter - %@, and parameter - %@ [B]"; // B - buldle
13+
14+
// Plural key that also exists in Localizable.stringsdict - this simulates what Crowdin exports
15+
// The plural forms in .stringsdict should take precedence over this simple string
16+
"johns_pineapples_count" = "John has pineapples"; // Fallback singular form

Tests/UnitTests/BundleStringTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,29 @@ class BundleStringTests: XCTestCase {
2121
XCTAssert("test_key_with_string_parameter".cw_localized(with: ["value"]) == "test value with parameter - value [B]")
2222
XCTAssert("test_key_with_int_parameter".cw_localized(with: [1]) == "test value with parameter - 1 [B]")
2323
}
24+
25+
func testPluralLocalizationWithKeyInBothFiles() {
26+
// Test for issue #347: When a key exists in both .strings and .stringsdict files,
27+
// plural forms should take precedence over the simple string
28+
29+
// The key "johns_pineapples_count" exists in both:
30+
// - Localizable.strings: "John has pineapples" (simple fallback string)
31+
// - Localizable.stringsdict: proper plural definitions (zero/one/other)
32+
33+
// Format for zero (should use plural from stringsdict, not simple string)
34+
let formatZero = NSLocalizedString("johns_pineapples_count", comment: "")
35+
let stringZero = String.localizedStringWithFormat(formatZero, 0)
36+
XCTAssert(stringZero == "John has no pineapples", "Zero plural form should work: '\(stringZero)'")
37+
38+
// Format for one (should use plural from stringsdict)
39+
let formatOne = NSLocalizedString("johns_pineapples_count", comment: "")
40+
let stringOne = String.localizedStringWithFormat(formatOne, 1)
41+
XCTAssert(stringOne == "John has 1 pineapple", "Singular plural form should work: '\(stringOne)'")
42+
43+
// Format for multiple (should use plural from stringsdict)
44+
let formatMany = NSLocalizedString("johns_pineapples_count", comment: "")
45+
let stringMany = String.localizedStringWithFormat(formatMany, 5)
46+
XCTAssert(stringMany == "John has 5 pineapples", "Plural form should work: '\(stringMany)'")
47+
}
2448
}
2549

0 commit comments

Comments
 (0)