1

I'm trying to understand pluralization in Go. The example from the docs https://godoc.org/golang.org/x/text/message/catalog#hdr-String_interpolation does not work. The method plural.Select does not event exist. It should be plural.Selectf. Note the f at the end.

message.Set(language.English, "You are %d minute(s) late.",
catalog.Var("minutes", plural.Selectf(1, "one", "minute")),
catalog.String("You are %d ${minutes} late."))
p := message.NewPrinter(language.English)
p.Printf("You are %d minute(s) late.", 5)

I found another tutorial here https://phraseapp.com/blog/posts/internationalization-i18n-go/. This code works fine

message.Set(language.English, "You have %d problem",
    catalog.Var("minutes", plural.Selectf(1, "%d", "one", "minute", "other", "minutes")),
    catalog.String("You are %d ${minutes} late."))
printer := message.NewPrinter(language.English)
printer.Printf("You have %d problem", 1)
printer.Println()
printer.Printf("You have %d problem", 3)
printer.Println()

// result
// You are 1 minute late.
// You are 3 minutes late.

Both examples use the advanced string interpolation. Now I'm trying to understand plural.Selectf. What is the first argument 1 doing? Why do I need the second argument %d? I think I understand the rest

"one"  : "minute"
"other": "minutes"

I also saw %[1]d in catalog.String. What does this do?

Thanks a lot! I'm super confused right now.

zemirco
  • 16,171
  • 8
  • 62
  • 96

2 Answers2

4

Here you go!

The first argument of plural.Selectf is an int. So it can be any valid integer. The second argument is a string. It should be a format verb i.e %d or %s or %f The third argument is an empty interface, could receive any type i.e string, struct, catalog.Message, ..

Let's take this example,

func main() {

    var (
            msg = plural.Selectf(2, "%d",
                "=10", "%[1]d task and %[2]d processes remaining!", // interface 1
                "=1", "%[1]d task and %[2]d processes", // interface 2
                "other", "%d tasks and %d processes!" // interface 3
            )
            key = "%d task - %d process"
            tag = "en"
        )

        lTag := language.MustParse(tag)
        message.Set(lTag, key, msg)

        p := message.NewPrinter(language.English)
        p.Printf("%d task - %d process", 1, 10)

}

Here we have created a NewPrinter with language English and set the translation messages with the key %d task(s) remaining! for tag en(short code for English language).

When p.Printf("%d task - %d process", 1, 3) line executes, the translation mechanism takes the first argument(format specifier) i.e %d task - %d process and checks for the message by comparing with key we set in for en tag. If the key is found, then it processes the message i.e msg.

Here the Selectfs first argument says that take nth (i.e 2nd in our case) format verb's value from Printf (i.e 10 in the second value for %d) and compare with selector of cases i.e =10 in the case 1 (interface 1). If the comparison is success then returns the processed value i.e 1 task and 10 processes in case 1.

If the Printf receives values for 2nd %d other than 1 & 10 then it would return the value from case 3 (interface 3)

And,

The %[n]d could be used like,

fmt.Printf("%[2]d %[1]d\n", 11, 22) and it prints 22 11.

Hope, this helps you.

Thaadikkaaran
  • 5,088
  • 7
  • 37
  • 59
  • It starts making sense. I still do not understand the second argument to `plural.Selectf` `"%d"`. I've tried changing it and it seems like I can write anything I want there. I literally wrote "anything" and it still works. – zemirco Jul 18 '18 at 12:09
  • What i am aware of the second argument to `plural.Selectf` is that it accepts only `%d, %f, %g, %e`. If we provide other than these, it is simply ignoring the value. And this format verb is been used with catalog.Message type. – Thaadikkaaran Jul 19 '18 at 07:02
0

In addition to the accepted answer by Thaadikkaaran, regarding the purpose of the second parameter to plural.Selectf:

As stated in the documentation, "This is especially relevant for non-integer values".

The format string is applied to the argument before comparing it to the different "case" conditions, essentially allowing you to trim floating point values to the precision desired for the comparison.

message.Set(language.English, "%f%% battery", plural.Selectf(1, "%f",
    "<50", "Your battery is less than half full.",
    "other", "You still have enough battery left.",
))
p := message.NewPrinter(language.English)
p.Printf("%f%% battery", 49.99999)

fmt.Println()

message.Set(language.English, "%f%% battery", plural.Selectf(1, "%.4f",
    "<50", "Your battery is less than half full.",
    "other", "You still have enough battery left.",
))
p.Printf("%f%% battery", 49.99999)

prints

Your battery is less than half full.

You still have enough battery left.

Sam
  • 811
  • 5
  • 9