ktor-i18n icon indicating copy to clipboard operation
ktor-i18n copied to clipboard

Support CLDR Plurals and per-call MessageResolver

Open budius opened this issue 3 years ago • 3 comments

Is your feature request related to a problem? Please describe.

  • I need support for CLDR plural rules (https://www.unicode.org/cldr/cldr-aux/charts/27/supplemental/language_plural_rules.html) that's the format when exporting from phrase.com
  • I need support to use different resource bundle depending on a request header.

Describe the solution you'd like I would do the code and open a PR trying the best I can to not break any existing API but supporting the extra features.

  • extra parameter on the class R to define the type of plural stye to use (the current number based option/default) and CLDR based (the new option)
  • introduce a private val CallResolverKey = AttributeKey<ResourceBundleMessageResolver?>("CallResourceBundleMessageResolver") so that on the call to fun ApplicationCall.t( it would delegate the to a different resolver (with a different baseName)

Describe alternatives you've considered ¯\_(ツ)_/¯ I don't know forking the project maybe.

budius avatar Dec 08 '22 17:12 budius

Hi, thanks for filing the issue.

Regarding the first point, this makes total sense, however the R class wasn't meant to handle complex cases, the goal was that you can define your own custom key generator which resolves to the appropriate set of possible keys. Nonetheless, the current implementation isn't flexible enough to handle your use case as it's not possible to know the current locale when generating the keys, so I suggest the following:

Change the KeyGenerator to an abstract class that has a method which receives the current locale as a parameter. It would just be a replacement for the current iterator strategy and addresses its current shortcomings. For backwards compatibility, the new method should just fallback to calling the iterator.

For your second use case, the defaults provided with the library aren't meant to handle complex use cases, at its core the library just handles simple routing and locale resolution, you may extend it to accommodate your use case, by defining your own translation function:

fun ApplicationCall.tr(keys: Iterable<String>, vararg args: Any = arrayOf()) =
  attributes.computeIfAbsent(CallMessageResolverKey, {
    /* create the appropriate message resolver */ 
  }).t(locale, keys, *args)

This should handle your use case, but let me know if I misunderstood something.

You're welcome to open a PR with these changes if you'd like. I am going to implement the first one for sure but I don't know when exactly I'll have the time to do it.

aymanizz avatar Dec 09 '22 14:12 aymanizz

You're welcome to open a PR with these changes if you'd like. I am going to implement the first one for sure but I don't know when exactly I'll have the time to do it.

Hey, just a quick update here. About this issue and the other one I opened, I'll be working on them on the sprint starting 2nd January. I'll start looking into specific implementation details then and might pop some more messages here.

budius avatar Dec 29 '22 10:12 budius

hey, just as an update here.

At the end what this library implements and what we needed for the project was too different and I couldn't integrate it into this library and ended-up just rolling our own internally.

If you're interested in doing the translations in the future, I used this library here https://github.com/xyzsd/cldr-plural-rules which implements all the CLDR rules and the rest was pretty straight forward:

val rules = PluralRule.createOrDefault(locale, PluralRuleType.CARDINAL) // get a rule, this should be cached thou
val category = rules.select(9) // the quantity
val pluralKey = category.asString // just a simple extension method to transform the enum to string
"$baseKey.$pluralKey"

Good luck.

budius avatar Jan 10 '23 11:01 budius