scelis / twine

Twine is a command line tool for managing your strings and their translations.
Other
840 stars 151 forks source link

Support plural forms #46

Open jobi opened 10 years ago

jobi commented 10 years ago

A feature request, we would love it twine supported plural forms. Something like:

[delete_things]
    en:one = Delete this object?
    en:other = Delete these objects?

Android has support for plural forms in its string resources:

    <plurals
        name="plural_name">
        <item
            quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
            >text_string</item>
    </plurals>

iOS could use Smartling

gettext also has support for plural forms

scottdweber commented 10 years ago

A very good idea! We have actually talked about this briefly, but have not taken it any further than casual discussion.

yincrash commented 10 years ago

The way I currently do plurals in twine is having a separate key for each plural, such as cool_things_one cool_things_other, then do

    <plurals
        name="cool_things">
        <item quantity="one">@string/cool_things_one</item>
        <item quantity="other">@string/cool_things_other</item>
    </plurals>

in a separate xml file in values/ that isn't managed by twine.

jobi commented 10 years ago

@yincrash that's a pretty good solution, but that means we need to maintain the list of keys in 2 places

scelis commented 10 years ago

iOS and Mac actually support plurals natively as of 10.9 and iOS 7. You can read about this in the 10.9 Foundation Release Notes.

That said, plurals are tricky. One of the goals for Twine has been to maintain a simple feature set that all of the string file formats support. By fully supporting plural forms for Android, Mac, and iOS, we have to be careful that we don't prevent those strings from being written out to other formats like jQuery, gettext, etc.

I think the trick to implementing this feature properly is to use some sort of convention such that all strings can be written out to all formats. Maybe the twine syntax is modified as @jobi suggested. Plurals could be written out to formats that support it and those that don't will get multiple strings with well-defined suffixes like "delete_things__one", "delete_things__many", etc.

pichirichi commented 10 years ago

This is a great idea @scelis , I like this approach. This is even a challenge when sending strings for translation since most services don't support the plural format.

scelis commented 8 years ago

The more I think about this feature, the more I think that the benefits do not outweigh the additional complexity.

wujessica commented 8 years ago

We had this problem consolidating Android and iOS translations for our app and we ended up implementing it in a project fork (with some convention ideas from here!). We'd be happy to send a PR if you think that would be useful.

scelis commented 8 years ago

@wujessica Thanks for this! I am sure others may find it useful. It might be hard to merge into the main repo until we figure out how it functions with the other (non-iOS and Android) formatters.

mvarshavsky commented 7 years ago

@scelis - any way I can convince you to revisit your cost/benefit analysis above :) Any project will sooner or later encounter the need to pluralize. Having to maintain a separate per-platform file as @yincrash is doing it brings the problem twine is solving right back (granted, on a smaller scale). It seems like there must be a way to unlock this functionality for plural-aware formatters, while keeping it neutral for the rest (e.g., by convention only picking the first of key:qty occurrences). Also, if using up :blah just for this purpose it too greedy, a more generic scheme can be applied.

scelis commented 7 years ago

@mvarshavsky Is there really great cost to not using plurals in your string files? We use plurals in our apps all the time and it requires just a few lines of code given our twine string file:

if numItems == 1 {
    label.string = localizedString("NumItemsSingular", numItems)
} else {
    label.string = localizedString("NumItemsPlural", numItems)
}

So yeah, I don't think that the amount of complexity this would add to twine would be worth it just to turn the above chunk of code into:

label.string = localizedString("NumItemsWithPlurals", numItems)
mvarshavsky commented 7 years ago

A lot of time it's three: no items / 1 item / n items - but that's besides the point; in my eyes it's just ugly superfluous code that detracts from readability. My belief is that placeholders/interpolation (which twine has) and ability to pluralize is something that all decent-sized projects eventually need and therefore all platforms will eventually evolve to support. I get you're trying to keep complexity out - but that choice pushes it to all consumers of your library :) Anyway, I'm not here to argue, it's your decision, just chiming in with another "+1" on the feature request. We have @wujessica's fork to fall back on. Appreciate all the work you've put into this!

scottdweber commented 7 years ago

Another consideration is that, while the following code works just fine for English and some other languages, there are many languages where such logic may not be appropriate (e.g. Korean, Chinese, Russian, etc.). These cases are where the platform's in-built plurals system helps you, and requires a more robust set of translations.

if numItems == 1 {
  // singular
} else {
  // plural
}
scelis commented 7 years ago

Fair point. Once you need to support more than (0 | 1 | more) then it can get trickier. I would not be opposed to twine having decent plural support were a good PR to be submitted (which is why this feature request is still open), but it really is a huge undertaking and still not one that I am convinced is completely worth the effort.

The submitted PR would need to do the following:

txaiwieser commented 6 years ago

It would be awesome if the Android and iOS plural was merged to master.

tobilarscheid commented 6 years ago

my team is also looking for this feature, why was the PR closed?

greshilov commented 6 years ago

We've implemented this functionality in our fork here: https://github.com/mapsme/twine Usage:

[placepage_summary_rating_description]
    en:one = Based on %d review
    en:other = Based on %d reviews
    ru:one = На основе %d отзыва
    ru:other = На основе %d отзывов
    ...

Plural strings works only for android and ios(see separate formatter apple-plural). Possible plural modificators. Hope this will be useful :)

TomGranot commented 6 years ago

@greshilov - Coming late into the conversation, can you elaborate a bit on what you did? I think I'm going to use something similar in my setup.

altagir commented 5 years ago

Hi @greshilov, I don't understand why you added apple-plural along with apple can't they be merged?

We've implemented this functionality in our fork here: https://github.com/mapsme/twine Usage:

greshilov commented 5 years ago

@altagir if I'm not mistaken it was difficult to me to use same logic in one formatter for iOs plural/non plural strings. Unlike android, apple strings are actually generating to different files (.strings, .stringsdict). This script here is using for generating all strings in mapsme, maybe helpful.

@tomgs looks like I've lost your question :( If you still have any questions feel free to ask.

altagir commented 5 years ago

@scelis , The fork from @greshilov works great for plural form (android and ios) could it be merged ? this feature indicates "needs code".

scelis commented 5 years ago

@altagir As far as I can tell, that code isn't really mergeable and was implemented as a one-off fork. I am still amenable to merging this feature into Twine proper, but my requirements from above still stand.

altagir commented 4 years ago

hi @greshilov on android translations, the character ' is not properly formatted in values/ A plural traduction of "don't ..." should be "don\'t..." else Android Studio cannot parse the xml only happens with plural form We use &apos; to handle correct output for ios and android

timthelion commented 1 year ago

@scelis Unfortunately your example:

if numItems == 1 {
    label.string = localizedString("NumItemsSingular", numItems)
} else {
    label.string = localizedString("NumItemsPlural", numItems)
}

doesn't work for all languages. For example in Czech genetive plural is different for0, 1, 2-4, and >4:

English: "1 banana" Czech: "1 banán"

English: "2 bananas" Czech: "2 banány"

English: "4 bananas" Czech: "4 banány"

English: "5 bananas" Czech: "5 banánů"

English: "100 bananas" Czech: "100 banánů"

scelis commented 1 year ago

@timthelion Yes, some languages are definitely more complicated. You should be able to take my 2 case example and extrapolate to N.

timthelion commented 1 year ago

I don't understand how you would do that. The only way that occurs to me to do this would be for the Czech translator would go and edit the code for every string that contains a plural and add a new string for greater than 4 for every language. That seems like a lot of work, and not all translators will know how to code.