25

As suggested here names of people should be capitalized like John William Smith.

I'm writing a small software in Golang which gets last and first name from user's form inputs.

Until Go 1.18 I was using:

lastname = strings.Title(strings.ToLower(strings.TrimSpace(lastname)))
firstname = strings.Title(strings.ToLower(strings.TrimSpace(firstname)))

It works but now Go 1.18 has deprecated strings.Title().

They suggest to use golang.org/x/text/cases instead.

So I think I should change my code in something like this:

caser := cases.Title(language.Und)

lastname = caser.Title(strings.ToLower(strings.TrimSpace(lastname)))
firstname = caser.Title(strings.ToLower(strings.TrimSpace(firstname)))

It works the same as before.

The difference is for Dutch word like ijsland that should be titled as IJsland and not Ijsland.

The question

In the line caser := cases.Title(language.Und) I'm using Und because I don't know what language Tag to use.

Should I use language.English or language.AmericanEnglish or other?

So far it was like strings.Title() was using Und or English?

Fred Hors
  • 3,258
  • 3
  • 25
  • 71
  • 1
    I was confused by the question - this isn't a difference in behavior between `strings.Title` and `cases.Title`, but a lack of a custom implementation for Dutch. You are correct that there is no such implementation in the standard library; you'll likely need to implement your own if you require that functionality. – Adrian Mar 25 '22 at 17:47
  • I do not understand the answer. I'm asking about what language I should use. – Fred Hors Mar 25 '22 at 18:00
  • And what was/is the "language" of `strings.Title`? – Fred Hors Mar 25 '22 at 18:36
  • In theory you can use English as default language and try to infer the language using something from your domain. For instance, if you read from database perhaps you can add a field “language”. Or the language can be a parameter. – Tiago Peczenyj Mar 25 '22 at 19:53
  • However you should ask yourself if your approach to names is the right one. firstname/lastname are not universal. If you handle asian or middle-east names this will be complex. Perhaps you should use a field “full name” and only Trim(), and add an optio field where people can add a first name to be informally called (or nickname). – Tiago Peczenyj Mar 25 '22 at 19:54
  • 1
    If you are going to take the time to find a replacement for strings.Title, you might also want to examine the assumption that title case is appropriate. –  Mar 25 '22 at 19:56
  • @Zombo are you talking about my idea to titleize names? – Fred Hors Mar 25 '22 at 20:06
  • Consider names like Werner von Braun, LeVar Burton, Douglas MacArthur, etc.. `strings.Title(strings.ToLower(n))` mangles these names. –  Mar 25 '22 at 20:21
  • @Zombo. You're right. What to do? Do you have any hint? I don't want to leave to the user this job because people is not good at this. – Fred Hors Mar 25 '22 at 20:27

4 Answers4

18

As mentioned in documentation strings.Title is deprecated and you should use cases.Title instead.

Deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.

Here is an example code of how to use it as from two perspectives:

// Straightforward approach
caser := cases.Title(language.BrazilianPortuguese)
titleStr := caser.String(str)

// Transformer interface aware approach
src := []byte(s)
dest := []byte(s) // dest can also be `dest := src`
caser := cases.Title(language.BrazilianPortuguese)
_, _, err := caser.Transform(dest, src, true)

Make sure to take a look on the transform.Transformer.Transform and cases.Caser in order to understand what each parameter and return values mean, as well as the tool's limitations. For example:

A Caser may be stateful and should therefore not be shared between goroutines.

Regarding what language to use, you should be aware of their difference in the results, besides that, you should be fine with any choice. Here is a copy from 煎鱼's summary on the differences that cleared it for me:

Go Playground: https://go.dev/play/p/xp59r1BkC9L

func main() {
    src := []string{
        "hello world!",
        "i with dot",
        "'n ijsberg",
        "here comes O'Brian",
    }
    for _, c := range []cases.Caser{
        cases.Lower(language.Und),
        cases.Upper(language.Turkish),
        cases.Title(language.Dutch),
        cases.Title(language.Und, cases.NoLower),
    } {
        fmt.Println()
        for _, s := range src {
            fmt.Println(c.String(s))
        }
    }
}

With the following output

hello world!
i with dot
'n ijsberg
here comes o'brian

HELLO WORLD!
İ WİTH DOT
'N İJSBERG
HERE COMES O'BRİAN

Hello World!
I With Dot
'n IJsberg
Here Comes O'brian

Hello World!
I With Dot
'N Ijsberg
Here Comes O'Brian
Eduardo Pacheco
  • 504
  • 4
  • 14
6

So far it was like strings.Title() was using Und or English?

strings.Title() works based on ASCII, where cases.Title() works based on Unicode, there is no way to get the exact same behavior.

Should I use language.English or language.AmericanEnglish or other?

language.English, language.AmericanEnglish and language.Und all seem to have the same Title rules. Using any of them should get you the closest to the original strings.Title() behavior as you are going to get.

The whole point of using this package with Unicode support is that it is objectively more correct. So pick a tag appropriate for your users.

Dylan Reimerink
  • 5,874
  • 2
  • 15
  • 21
3

strings.Title(str) was deprecated, should change to cases.Title(language.Und, cases.NoLower).String(str)

package main
import (
    "fmt"
    "strings"
    "golang.org/x/text/cases"
    "golang.org/x/text/language"
)
func main() {
    fmt.Println(strings.Title("abcABC"))                                   // AbcABC
    fmt.Println(cases.Title(language.Und, cases.NoLower).String("abcABC")) // AbcABC
}

Playground : https://go.dev/play/p/i0Eqh3QfxTx

Vivian K. Scott
  • 515
  • 2
  • 5
  • 13
1

Here is a straightforward example of how to capitalize the initial letter of each string value in the variable using the golang.org/x/text package.

package main

import (
    "fmt"

    "golang.org/x/text/cases"
    "golang.org/x/text/language"
)

func main() {
    sampleStr := "with value lower,  all the letters are lowercase. this is good for poetry perhaps"
    caser := cases.Title(language.English)
    fmt.Println(caser.String(sampleStr))
}

Output : With Value Lower, All The Letters Are Lowercase. This Is Good For Poetry Perhaps

Playground Example: https://go.dev/play/p/_J8nGVuhYC9