Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Formatted plurals #33

Merged
merged 3 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

## master

### Added
- Add support for formatted plurals ([#33](https://github.com/AckeeCZ/ACKLocalization/pull/33), kudos to @leinhauplk)

## 1.3.0

### Added
Expand Down
17 changes: 15 additions & 2 deletions Sources/ACKLocalizationCore/Model/PluralRuleWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,40 @@ struct PluralRuleWrapper {
}

extension PluralRuleWrapper: Codable {
private enum Constants {
static let stringFormatSpecifier = "%s"
}

func encode(to encoder: Encoder) throws {
// Since the stringDict is completely dynamic
// we cannot use some predefined `struct` to encapsulate the data.
// We need to use `CustomKey` that can take any string as a key.
var container = encoder.container(keyedBy: CustomKey.self)

// If the plural contains string format specifier, then use the positional arguments.
let containsStringSpecifier: Bool = translations.contains(where: {
$0.value.lowercased().contains(Constants.stringFormatSpecifier)
} )
let formatKey = containsStringSpecifier ? "%1$#@inner@" : "%#@inner@"

var items = [
// Mandatory key and the only option
"NSStringFormatSpecTypeKey": "NSStringPluralRuleType",
"NSStringFormatValueTypeKey": "d"
]

translations.forEach {
items[$0.key.rawValue] = $0.value
items[$0.key.rawValue] = $0.value.replacingOccurrences(
of: Constants.stringFormatSpecifier,
with: "%2$@"
)
}

// StringsDicts use variables by default.
// We need to specify the variable that is then
// replaced by the plural rule.
// In this case the variable is `inner` and it's hardcoded.
try container.encode("%#@inner@", forKey: .init(stringValue: "NSStringLocalizedFormatKey"))
try container.encode(formatKey, forKey: .init(stringValue: "NSStringLocalizedFormatKey"))
try container.encode(items, forKey: .init(stringValue: "inner"))
}
}
54 changes: 54 additions & 0 deletions Tests/ACKLocalizationCoreTests/ACKLocalization+Plurals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,58 @@ final class ACKLocalizationPluralsTests: XCTestCase {
XCTAssertEqual(error as? PluralError, PluralError.invalidPluralRule(rows[0].key))
}
}

func testPluralWithStringFormatSpecifier() throws {
// Given
let rows = [
LocRow(key: "key##{many}", value: "%d many"),
]

let expectedResult: [String: Encodable] = [
"NSStringLocalizedFormatKey": "%#@inner@",
"inner": [
"NSStringFormatSpecTypeKey": "NSStringPluralRuleType",
"NSStringFormatValueTypeKey": "d",
"many": "%d many"
]
]
let expectedResultEncoded = try JSONSerialization
.data(withJSONObject: expectedResult, options: .sortedKeys)

// When
let plurals = try ackLocalization.buildPlurals(from: rows)
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
let encodedData = try encoder.encode(plurals.first?.value)

// Then
XCTAssertEqual(encodedData, expectedResultEncoded)
}

func testPluralWithIntegerFormatSpecifier() throws {
// Given
let rows = [
LocRow(key: "key##{many}", value: "%s many"),
]

let expectedResult: [String: Encodable] = [
"NSStringLocalizedFormatKey": "%1$#@inner@",
"inner": [
"NSStringFormatSpecTypeKey": "NSStringPluralRuleType",
"NSStringFormatValueTypeKey": "d",
"many": "%2$@ many"
]
]
let expectedResultEncoded = try JSONSerialization
.data(withJSONObject: expectedResult, options: .sortedKeys)

// When
let plurals = try ackLocalization.buildPlurals(from: rows)
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
let encodedData = try encoder.encode(plurals.first?.value)

// Then
XCTAssertEqual(encodedData, expectedResultEncoded)
}
}