Open susi021 opened 9 months ago
Rswift works with .stringsdict files from Xcode, see: https://developer.apple.com/documentation/xcode/localizing-strings-that-contain-plurals
Alternatively, as of Xcode 15, you can use a strings catalog: https://developer.apple.com/documentation/xcode/localizing-and-varying-text-with-a-string-catalog
I suggest you first try to make your required behaviour work without Rswift, using the features from the platform, before adding Rswift code generation.
Thanks for the advice @tomlokhorst!
To make use of automatic grammar agreement, I will retrieve the localizable string without relying on R.swift for handling plurals.
Additionally, I'm curious to know if R.swift has any plans to support String Catalog, or even implement automatic grammar agreement?
"%lld car" = "^[%lld car](inflect: true)";
To generate a R.swift method for this key, we should be able to 1- access it by the key 2- pass arguments 3- generate an AttributedString from it. (inflect system is a a feature of AttributedString)
The localization system of Swift, use ExpressibleByStringInterpolation everywhere.
LocalizedStringResource String.LocalizationValue LocalizedStringKey all of these use ExpressibleByStringInterpolation to create necessary information to access and format localized string.
The problem is none of them have any other init to pass key and argument types.
I achieved this by utilizing LocalizedStringResource.init with defaultValue which requires iOS 16.
let carKey: LocalizedStringResource = .init("%lld car", defaultValue: "\(2)")
print(String(AttributedString(localized: carKey).characters))
// prints: 2 cars
I tried other examples.
On this case there are multiple plural and the order of objects are different on different localizations.
// English
"pen_and_book" = "^[%1$lld pen](inflect: true) and ^[%2$lld book](inflect: true) ";
// French
"pen_and_book" = "^[%2$lld livre](inflect: true) and ^[%1$lld stylo](inflect: true) ";
let enKey: LocalizedStringResource = .init("pen_and_book", defaultValue: "\(1)\(2)", locale: .init(identifier: "en"))
print(String(AttributedString(localized: enKey).characters))
// prints: 1 pen and 2 books
let frKey: LocalizedStringResource = .init("pen_and_book", defaultValue: "\(1)\(2)", locale: .init(identifier: "fr"))
print(String(AttributedString(localized: frKey).characters))
// prints: 2 livres et 1 stylo
This method can also replace NSLocalizedString completly (after iOS 16)
"hello_with_name" = "Hello %@!";
let key: LocalizedStringResource = .init("hello_with_name", defaultValue: "\("World")")
print(String(localized: key))
// prints: Hello World!
This is a prelimenery extensions to convert Rswift String resources to String and Attributed String.
@available(iOS 16, *)
extension RswiftResources.StringResource1 {
// defaultValue does not support CVarArg protocol. I could not find a suitable protocol.
func localizedStringResource(_ arg1: Arg1) -> LocalizedStringResource where Arg1 == String {
switch source {
// Cannot pass bundle inside of source. Its type is different.
case let .selected(_, locale):
return .init(self.key, defaultValue: "\(arg1)", table: self.tableName, locale: locale, comment: self.comment)
default:
return .init(self.key, defaultValue: "\(arg1)", table: self.tableName, comment: self.comment)
}
}
func localizedStringResource(_ arg1: Arg1) -> LocalizedStringResource where Arg1 == Int {
switch source {
case let .selected(_, locale):
return .init(self.key, defaultValue: "\(arg1)", table: self.tableName, locale: locale, comment: self.comment)
default:
return .init(self.key, defaultValue: "\(arg1)", table: self.tableName, comment: self.comment)
}
}
}
@available(iOS 16, *)
extension LocalizedStringResource {
var asString: String {
.init(localized: self)
}
var asAttributedString: AttributedString {
.init(localized: self)
}
}
extension AttributedString {
var asString: String {
.init(self.characters)
}
}
How to use:
print(R.string.localizable.lldCar.localizedStringResource(5).asAttributedString.asString)
// prints: 5 cars
print(R.string.localizable(preferredLanguages: ["fr"]).lldCar.localizedStringResource(5).asAttributedString.asString)
// prints: 5 voitures
Currently there is SwiftUI extension of Rswift, however, they lose inflect information.
This implementation fixes that problem.
@available(iOS 16, *)
extension Text {
public init<Arg1: CVarArg>(_ resource: StringResource1<Arg1>, _ arg1: Arg1) where Arg1 == String {
self.init(resource.localizedStringResource(arg1))
}
public init<Arg1: CVarArg>(_ resource: StringResource1<Arg1>, _ arg1: Arg1) where Arg1 == Int {
self.init(resource.localizedStringResource(arg1))
}
}
Text(R.string.localizable.lldCar, 5)
// displays: 5 cars
For example, when I define the string
"%lld person" = "^[%lld person](inflect: true)";
in localizable.string, and call it with theR.string.localizable.lldPerson(2)
, it will return^[2 person](inflect: true)
, not the2 people
as I expected.To leverage the automatic grammar agreement, I need to define the following:
Use the R.swift to get the translation:
So that it will return "2 people", which is translated and pluralized.
Is there any better way to do that?