PicnicSupermarket / localicious

A toolchain for working with localization files in a platform-agnostic way.
MIT License
31 stars 9 forks source link

Generate additional function without args for quantity strings #46

Open jhildensperger opened 3 years ago

jhildensperger commented 3 years ago

Let's take an example of formatting a string for a number of things. The yml could look like this:

ThingCount:
    COPY:
        ZERO:
            en: 'no things'
        ONE:
            en: '%1{{d}} thing'
        OTHER:
            en: '%1{{d}} things

That would generate a function like this:

public static func ThingCount(quantity: Int, args: CVarArg...) -> LocaliciousData {
    ...
}

And you would use it like this:

let numberOfThings: Int = 10
L.Base.ThingCount(quantity: numberOfThings, args: numberOfThings)

My assumption is that the most common use case for strings with number formats would be a single arg so it is a little weird to pass the same arg twice. A nice solution could be to also generate a function that calls the current function with the quantity param as the args like this:

public static func ThingCount(quantity: Int) -> LocaliciousData {
    return ThingCount(quantity: quantity, args: quantity) 
}

Then you get a nice clean looking usage for the simple, single arg case.

let numberOfThings: Int = 10
L.Base.ThingCount(quantity: numberOfThings)
LcTwisk commented 3 years ago

Hey @jhildensperger, Sorry for not getting back to you earlier. Looking at our internal usages for quantity strings (plurals), there's only a single occurrence where we pass in the same variable as quantity AND in args, while we have quite a bunch of quantity strings.

However, I definitely see this could be a nice improvement. Not sure if we need to generate additional methods though. Even for the methods that are generated right now, we can already leave out the args param, and use it like this:

L.Base.ThingCount(quantity: numberOfThings)

We can then patch the translation method in the template here, to fallback to the quantity if no arguments are passed in:

func translation(
    forKey key: String,
    withBundle bundle: Bundle = Bundle.main
) -> String? {
    let value = bundle.localizedString(forKey: key, value: nil, table: nil)
    guard value != key else {
    return nil
    }
    var translationArgs = self.translationArgs ?? []
+   if let quantity = self.quantity, translationArgs.isEmpty {
+       translationArgs = [quantity]
+   }
    guard translationArgs.count > 0 else { return value }
    return String(format: value, arguments: translationArgs)
}

This should work, but only for format strings containing just a single placeholder. What do you think?

jhildensperger commented 3 years ago

On second look, it would be better if iOS plurals used stringsdict files instead of the current solution putting them in the strings file. With stringsdict you also get support for TWO, FEW, and MANY which some languages will require. With this, you would probably remove quantity altogether and just use args as you would arguments in String(format:arguments:) for iOS strings.

I have it working on this branch (but it needs some tests/examples for TWO, FEW, and MANY).

https://github.com/jhildensperger/localicious/tree/feature/ios-stringsdict-support