Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/text: localization support #12750

Open
mpvl opened this issue Sep 25, 2015 · 45 comments
Open

x/text: localization support #12750

mpvl opened this issue Sep 25, 2015 · 45 comments
Assignees
Labels
Milestone

Comments

@mpvl
Copy link
Member

@mpvl mpvl commented Sep 25, 2015

This issue is intended as an umbrella tracking issue for localization support.

Localization support includes:

  • formatting values, such as numbers, currencies, and dates in a language- and region-specific way.
  • marking text to be translated in fmt-ed text and templates.
  • integration into translation pipeline

Details to be covered in design docs.

@mpvl mpvl self-assigned this Sep 25, 2015
@maximilien
Copy link

@maximilien maximilien commented Sep 29, 2015

@mpvl
Copy link
Member Author

@mpvl mpvl commented Sep 29, 2015

@maximilien: i18n4go does not address localized formatting of values like numbers and I think it will be difficult to retrofit it properly. In case of selecting translation variants based on the linguistic features of the arguments, you'll end up with the same struggle one witnesses with localization frameworks for other languages.
Also, i18n4go extracts all strings and then uses an exclusion file. This may work well for command line tools or applications where most strings need localization, but this is not the norm. It breaks down when a large number of the strings in code do not need to be localized. For example, internal error messages are often not localized and may actually be the bulk of the text.
Addressing both issues will likely result in a different API, for example like the one proposed. The implementation of the proposed API is more complex, but it eliminates the need to generate a parallel version of the code and T wrappers.

This proposal is fairly agnostic about translation pipelines, though. So it may be possible to fit this proposal on top of the i18n4go translation pipeline. Seems like a convenient first target.

@infogulch
Copy link

@infogulch infogulch commented Sep 30, 2015

Using the Printf of message.Printer has the following consequences:

  • ...
  • the format string is now a key used for looking up translations

Is the format string by itself sufficient for determining the context? I can imagine a very simple Printf used like m.Printf("%s: %d", m, i) where the format string %s: %d could appear a dozen times throughout a codebase with very different contexts. (You could argue that this is a very poor format string to begin with, but it still demonstrates my concern.)

I must admit I'm not very familiar with localization problems and this may not be an issue in practice.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Sep 30, 2015

@infogulch It is indeed not enough. In my provisionally worked out API I do define a Key function that can be used for things like adding meaning and alternatives. I left it out of the design doc to not go into details too much. (I also stripped about 1/3rd of my original draft; maybe I went a bit overboard.)

Note that as the string has no meaning in itself, you could always write the format string as, for example, "Archive (verb)" and "Archive (noun)" and supply a "translation" for these in English ("Archive" for both). But this does not address all concerns. A more general solution:

Printf would have the following signature:

func (p *Printer) Printf(key Reference, args ...interface{}) (n int, err error) {

where Reference is either a string or a result from a func like

func Key(id string, fallback ...string) Reference {

This allows the familiar Printf() usage while addressing the concerns you raised. Many localization frameworks have a solution of a similar nature.

But the example string you provide does raise another good point: there may be format strings one does not want to translate at all while still using the message package to substitute localized values. This is possible as is (e.g. fmt.Printf("%s: %d", m.Print(m), m.Print(i))), but may be a bit clunky. A bit better may be something like m.Printf(message.Raw("%s: %d"), m, i), where the use of Raw makes extraction skip the string. I don't think there are too many cases where this is used, though. Even "%s: %d" will vary per language. But single-value substitutions like "%2.3d" should probably be excluded from translation.

@akavel
Copy link
Contributor

@akavel akavel commented Oct 1, 2015

As far as plurals are concerned, I've seen some elaborate examples, but what somewhat skimming the doc, it seems they can use only "<", ">", and "=" operators; I didn't read it 100% thoroughly however, so I may be wrong. I'll thus let myself ask here for clarifications: are the proposed mechanisms enough to cater for the rule for e.g. Polish language? In a version I found on Weblate site, it's described as [1] [2]:

n==1 ? 0 :                                              // "single"
 n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 :    // "few"
 2                                                      // "many"

which seems to me quite fine, correctly giving e.g.:

1 orangutan
2-4 orangutany
5-9 orangutanów
10, 11, 12, ..., 21 orangutanów
22 orangutany
101 orangutanów
102 orangutany
etc.
@mpvl
Copy link
Member Author

@mpvl mpvl commented Oct 1, 2015

@akavel: one should distinguish selectors from the rules you mention. The rules you refer to (which are defined in CLDR) would be used by the plural package to map numbers to a small set of plural categories (in the case of your example: single, few and many). The selectors subsequently pick alternatives based on these simplified categories. The maximum number of such categories, IIRC, is 6 (e.g. for Arabic).
Most localization frameworks that support plural, allow selecting on these categories only. ICU adds selecting on the number value (using "="). The matching algorithm defined in this proposal is a bit different from ICU, allowing also for Vars and selecting on "<" and ">". The selectors will often be generated or written by translators (with the help of a GUI) so they should remain simple.

In my foreseen implementation, it is really up to the feature implementation to interpret selectors. This means that there is a lot of flexibility in supporting wild feature value matching. However, if one looks at linguistic grammars like LFG and HSPG, which use many more features, the set of possible feature values is usually small.

The doc is indeed a bit sparse here (as well as all other topics, really).

@maximilien
Copy link

@maximilien maximilien commented Oct 5, 2015

@mpvl, sounds good. Happy to try and integrate once you have something ready to try. Best.

@abourget
Copy link

@abourget abourget commented Dec 31, 2015

Have you guys seen this one? https://github.com/nicksnyder/go-i18n seems pretty solid at first sight.

@abourget
Copy link

@abourget abourget commented Dec 31, 2015

It uses JSON as its base format, has tooling to help with the translation workflows

@BenLubar
Copy link

@BenLubar BenLubar commented Mar 27, 2016

By the way, I submitted some formatting fixes for the proposal doc a few weeks ago.

https://go-review.googlesource.com/19753

Not sure what I was supposed to do to get it reviewed.

@morriswinkler-simple
Copy link

@morriswinkler-simple morriswinkler-simple commented Nov 22, 2016

Any updates on how far the proposal is implemented in x/text/language, I find it a bit hard to figure out if this is anywhere near production readiness.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Nov 29, 2016

x/text/language is definitely production ready. But if you mean the specific functionality of this issue, it is still under development. Lately the focus had been more on other parts, my intention for the upcoming months to specifically focus on segmentation and this.

That said, string substitution is available with limited functionality, so you could play around with it. I recently checked in a tool to extract strings.

@morriswinkler-simple
Copy link

@morriswinkler-simple morriswinkler-simple commented Nov 29, 2016

Thanks for your reply, I have so far only used x/text/language in production and coded something around it that translates and formats messages for different countries. Just wanted to check if the language API is still up for changes.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Nov 29, 2016

No plans to change. Works well enough to the point it is not useful breaking people.

@MickMonaghan
Copy link

@MickMonaghan MickMonaghan commented Dec 20, 2016

Hi ,
Which package handles localized formatting/display of dates/times - or is this functionality not yet complete?

@MickMonaghan
Copy link

@MickMonaghan MickMonaghan commented Jan 4, 2017

Hi @mpvl, others,
I'm using x/text/collate to test the sorting of some random strings.
Below I use a Korean collator.

import (
  "fmt"
  "golang.org/x/text/collate"
  "golang.org/x/text/language"
)
func main() {
  strs := []string{"boef", "音声認識", "音声認識1", "aaland", "amsterdam", "월요일", "日付と時刻"}
  cl := collate.New(language.Korean) //Korean collator
  cl.SortStrings(strs)
  fmt.Println(strs)
}

Output: [aaland amsterdam boef 월요일 音声認識 音声認識1 日付と時刻]

If I use ICU to sort these strings (using level 3 strength), then I get the strings back like this:

[월요일 音声認識 音声認識1 日付と時刻 aaland amsterdam boef]

Am I setting up the collator incorrectly?
I'm using v1.8beta.

@morriswinkler-simple
Copy link

@morriswinkler-simple morriswinkler-simple commented Jan 12, 2017

Hello @MickMonaghan,

look slike there is not so much interest in this discussion, I just add my findings so far.

I looked into the collate code and could not really figure how the sorting is made up.
There are some byte blocks that are loaded by offsets, no idea how they work. I had also not so much time to figure that. So if someone likes to explain how that actually works I would be grateful.

I asked a Japanese friend of mine how he would sort a list of German and Japanese cities.
This is what he came up with.

img_9351

So he either converts the Japanese into Latin or the Latin into Japanese alphabet and sorts it then. I think that is also a good way to sort this list, first translate the syllables into the other alphabet and then sort it correspondingly.

@MickMonaghan
Copy link

@MickMonaghan MickMonaghan commented Jan 16, 2017

Hey @morriswinkler-simplesurance - thanks for the response.
I'm not entirely concerned with how it works, more concerned with does it work.
In some situations the collator clearly does work:

strs := []string{"champion", "humble"}
cl := collate.New(language.Slovak)
cl.SortStrings(strs)
//this correctly sorts 'champion' *after* 'humble' - as expected in Slovak

With a Korean sort, the Latin characters should be sorted after the Korean characters. But that's not happening.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Feb 7, 2017

@MickMonaghan: the implementation is based on the CLDR UCA tables. If I look at the collation elements of both the DUCET (Unicode's tables) and CLDR (the tailorings) they both show Hangul to have a higher primary collation value then Latin. So that explains why Korean is sorted later.

What is probably happening in ICU is that the the script for the selected language is sorted before other scripts. The Go implementation currently does not support script reordering, though. This is an TODO, but depends on changing the implementation to using fractional weights. This is a huge change and may take a while.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Feb 8, 2017

@MickMonaghan: I suggest you file a separate issue for this so it can be tracked individually.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Feb 8, 2017

@MickMonaghan: dates/times is on the list, but only after number etc. is completed.

@MickMonaghan
Copy link

@MickMonaghan MickMonaghan commented Feb 14, 2017

Thanks @mpvl , I'll log the collation bug

@Draqir
Copy link

@Draqir Draqir commented Jun 3, 2017

I started trying out golang seriously today to create a small application just for fun. However when I tried to localize my little application I didn't figure out any good solution. I just got a big headache. This is what I would do normally in TypeScript

export const Exceptions = {
    "AuthenticationError": {
        "Invalid": {
            "en-GB": "Invalid username or password",
            "sv-SE": "Fel användarnamn eller lösensenord"
        },
        "Required": {
            "en-GB": "You must be authenticated to see this resource",
            "sv-SE": "Du måste vara inloggad för att se denna resurs"
        }
    }
}

export class AuthenticationError extends Error {
    constructor(language:  "en-GB" | "sv-SE", message: "Invalid" | "Required") {
        super(Exceptions.AuthenticationError[message][language]);
    }
}

I would get errors if I typed any string wrong and it would simply just work. I tried to do something similar in go but the pain just got unbearable,

package localization

type labels struct {
	enGB string
	svSE string
}

type authenticationErrorMessages struct {
	Invalid  labels
	Required labels
}

type exceptionMessages struct {
	authErrors authenticationErrorMessages
}

// ExceptionMessage damnit, need to write a comment in an odd way.
func ExceptionMessage(language string, category string, exceptionType string, params []string) string {
	var exceptionMsg = exceptionMessages{
		authErrors: authenticationErrorMessages{
			Invalid: labels{
				enGB: "Invalid username or password",
				svSE: "Fel användarnamn eller lösenord",
			},
			Required: labels{
				enGB: "You must be authenticated to see this resource",
				svSE: "Du måste vara inloggad för att se denna resurs",
			},
		},
	}

	switch category {
	case "AuthenticationError":
		switch category {
		case "Invalid":
			switch language {
			case "enGB":
				return exceptionMsg.authErrors.Invalid.enGB
			case "svSE":
				return exceptionMsg.authErrors.Invalid.svSE
			}
		case "Required":
			switch language {
			case "enGB":
				return exceptionMsg.authErrors.Required.enGB
			case "svSE":
				return exceptionMsg.authErrors.Required.svSE
			}
		}
	}

	return "Error message not found"
}

// AuthenticationError damnit, need to write a comment in an odd way.
func AuthenticationError(message string) string {
	return ExceptionMessage("enOps", "AuthenticationErrors", message)
}

TL;DR

  • Go solution contains Magical strings
  • Go solution has no auto completion
  • Go solution is three times larger

So far everything has been really smooth writing golang code but this is just painful. I've tried out some localization packages as well but that hasn't worked out well so far. I'm of course not an expert in go after less than a day, maybe I missed something obvious in the language specification when I went through it this morning but regardless I'd really like to see some progress on this issue.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Jun 6, 2017

This is generally not how I would approach localization in Go (or any language), but going with your approach, I would do the following in go:

package main

import (
	"fmt"

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

type AuthenticationError string

const (
	ErrInvalid  AuthenticationError = "ErrInvalid"
	ErrRequired AuthenticationError = "ErrRequired"
)

func (e AuthenticationError) String() string { return string(e) }
func (e AuthenticationError) ErrorMessage(t language.Tag) string {
	p := message.NewPrinter(t)
	return p.Sprintf(e.String())
}

func init() {
	message.SetString(language.English, ErrInvalid.String(), "Invalid username or password")
	message.SetString(language.Swedish, ErrInvalid.String(), "Fel användarnamn eller lösensenord")

	message.SetString(language.English, ErrRequired.String(), "You must be authenticated to see this localized resource")
	message.SetString(language.BritishEnglish, ErrRequired.String(), "You must be authenticated to see this localised resource")
	message.SetString(language.Swedish, ErrRequired.String(), "Du måste vara inloggad för att se denna resurs")
}

func main() {
	fmt.Println(ErrRequired.ErrorMessage(language.Make("en-US")))
	fmt.Println(ErrRequired.ErrorMessage(language.Make("en-gb-oed")))
	fmt.Println(ErrRequired.ErrorMessage(language.Make("sv-FI")))

	// Output:
	// You must be authenticated to see this localized resource
	// You must be authenticated to see this localised resource
	// Du måste vara inloggad för att se denna resurs
}

This solves your concern of magical strings and auto-completion (at least from what I understand what you mean). It also implements fallbacks allowing for partial dictionaries, for example to only define translations where they differ for British and American english. (Notice the extra "localized" adjective in your message which I took the liberty to add for demonstrative purposes.)

But a more proper and scalable approach would be:

package main

import (
	"fmt"

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

type AuthenticationError int

const (
	ErrInvalid AuthenticationError = iota
	ErrRequired
)

func (e AuthenticationError) ErrorString(p *message.Printer) string {
	switch e {
	case ErrInvalid:
		return p.Sprintf("Invalid username or password")
	case ErrRequired:
		return p.Sprintf("You must be authenticated to see this localized resource")
	}
	return "Unknown"
}


var matcher language.Matcher

func init() {
	insertGeneratedTranslations()
	matcher = language.NewMatcher(message.DefaultCatalog.Languages())
}

func main() {
	// Match does a lot of magic to find the best language for the user.
	lang, _, _ := matcher.Match(language.Make("en-GB-oed"))
	p := message.NewPrinter(lang)

	fmt.Println(ErrInvalid.ErrorString(p))
	fmt.Println(ErrRequired.ErrorString(p))

	// Output:
	// Invalid username or password
	// You must be authenticated to see this localised resource
}

The gotext extract command (golang.org/x/text/cmd/gotext) can then extract the strings that need translation and will spit out textdata/gotext_en.out.json:

[
    {
        "original": {
            "msg": "Invalid username or password"
        },
        "translation": {},
        "position": "main/main.go:20:19"
    },
    {
        "original": {
            "msg": "You must be authenticated to see this localized resource"
        },
        "translation": {},
        "position": "main/main.go:22:19"
    }
]

This can then be used to create translations files for translators, the result of which can be used to generate code like the following.

func insertGeneratedTranslations() {
	// hardwired here, but can be read from file or generated tables.
	message.SetString(language.Swedish, "Invalid username or password", "Fel användarnamn eller lösensenord")
	message.SetString(language.BritishEnglish, "You must be authenticated to see this localized resource", "You must be authenticated to see this localised resource")
	message.SetString(language.Swedish, "You must be authenticated to see this localized resource", "Du måste vara inloggad för att se denna resurs")
}

In the future I hope to support gettext format and the like.
Note that "en" is the default language (by default) and its strings do not need to be translated. You may still want to "translate" English to English when one wishes to have conditional output for plurals and the like (work in progress, but in an advanced state). Of course you can also do this by hand.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Jun 6, 2017

And if succinctness is of importance, you could also do:

//go:generate stringer -type=AuthenticationError
package main

import (
	"fmt"

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

type AuthenticationError int

const (
	ErrInvalid AuthenticationError = iota
	ErrRequired
)

var matcher language.Matcher

func init() {
	insertGeneratedTranslations()
	matcher = language.NewMatcher(message.DefaultCatalog.Languages())
}

func main() {
	// Match does a lot of magic to find the best language for the user.
	lang, _, _ := matcher.Match(language.Make("en-GB-oed"))
	p := message.NewPrinter(lang)

	fmt.Println(p.Sprintf(ErrInvalid.String()))
	fmt.Println(p.Sprintf(ErrRequired.String()))
}

And have go generate create the Strings and use those as keys. This will require adding the English translations as well, though, and, more importantly, will disrupt a translation workflow as the translators will not have the original message and will make it hard to use gotext.
But if you use the SetStrings of the first example you're good to go.
I plan to add explicit support for errors, btw, which will make this a bit easier.

@maximilien
Copy link

@maximilien maximilien commented Jun 26, 2017

@mpvl this is looking good from small example above. I'd need to try to integrate it for better feedback.

In that vain, Is this ready for others to try and kick the tires? In other words, for me to try to support in i18n4go or shall I wait a bit more? Also, if yes, do I need to build custom Go version to get this feature or is there a built version I can use.

Not urgent, let me know when you have a chance. Best,

max

@mpvl
Copy link
Member Author

@mpvl mpvl commented Jun 27, 2017

@maximilien: I was indeed thinking I should contact you soon about integration, but you beat me to it. :) Up next is pluralization. It may be handy to wait until this is finished (quite close now), but up to you. If you think it is useful to sit together for a bit to crank this out next time I'm in the valley let me know.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Jun 27, 2017

FYI: the message package now has rudimentary number formatting support:

package main

import (
	"golang.org/x/text/language"
	"golang.org/x/text/message"
)

func main() {
	p := message.NewPrinter(language.Make("bn"))
	p.Printf("Numbers in Bengali: %d\n", 123456789)
	// Supports Unicode's 'u' extension of BCP-47 language tags.
	p = message.NewPrinter(language.Make("en-u-nu-fullwide"))
	p.Printf("Use full-width digits in English: %d\n", 123456789)

	// Output:
	// Numbers in Bengali: ১২,৩৪,৫৬,৭৮৯
	// Use full-width digits in English: 123,456,789
}
@BenLubar
Copy link

@BenLubar BenLubar commented Jun 27, 2017

It doesn't seem to support lojban:

package main

import (
        "fmt"

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

func main() {
        p := message.NewPrinter(language.Make("jbo"))
        p.Printf("Actual:   Numbers in lojban: %d\n", 123456789)
        fmt.Println("Expected: Numbers in lojban: parecivomuxazebiso")
        // Output:
        // Actual:   Numbers in lojban: 123,456,789
        // Expected: Numbers in lojban: parecivomuxazebiso
}

Quick reference:

.     ,     0    1    2    3    4    5    6    7    8    9
pi   ki'o  no   pa   re   ci   vo   mu   xa   ze   bi   so

A   B   C   D   E   F
dau fei gai jau xei vai

+    -    ∞
ma'u ni'u ci'i

2-3i
re ka'o ni'u ci

NaN
na'a'u
@mpvl
Copy link
Member Author

@mpvl mpvl commented Jun 27, 2017

@BenLubar The package currently does not support algorithmic or non-contiguous digits.
That said, looking at the rbnf and numberingSystem files in CLDR, neither does CLDR.
Once the RBNF methods are in it wouldn't be too hard to add, though.

Except maybe for the infix imaginary indicator. The 'i' is currently fixed and at a fixed position. Feel free to file a bug at http://unicode.org/cldr/trac. :)

@maximilien
Copy link

@maximilien maximilien commented Jun 29, 2017

@mpvl I'll wait a bit. NP. I know in CloudFoundry there is a push to refactor the CLI code which uses i18n4go so at a minimum we can wait for that to start and be sorted w.r.t. i18n.

If you think it is useful to sit together for a bit to crank this out next time I'm in the valley let me know.

Yup, sounds good. Just ping me when you have some dates in mind. Got various travels planned in Oct and Nov and vacation in Dec, but should be around before that. Cheers 🍻

@KarthikJay
Copy link

@KarthikJay KarthikJay commented May 1, 2018

Just wanted to know the status of the repo, especially that of the gotext tool.
It seems a lot of changes were made that don't match up with the docs, such as instead of a textdata directory I now get a locales folder, etc...

It seems that the gotext tool is broken as well currently preventing me from trying localization.

@mpvl
Copy link
Member Author

@mpvl mpvl commented May 2, 2018

The gotext tool is under active development and one of the main focuses at the moment. Progress is a bit bursts, but definitely active. A documentation overhaul is part of that.

@mvrhov
Copy link

@mvrhov mvrhov commented Jul 19, 2018

@mpvl: I'm looking at this and deciding if I want to use this or something else and manually format numbers/money/date . My users are a bit peculiar. What they usually want is the same behavior as in os. The language is set to e.g English, but other formatting is based on the country. Or even better overridden via some settings page also just like in OS.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Jul 19, 2018

@ericox
Copy link

@ericox ericox commented Feb 26, 2019

Is there a recommended way to localize [text|html]/templates per the proposal? I like the idea proposed, it doesn't seem to be implemented yet. Is that the case?

@mpvl
Copy link
Member Author

@mpvl mpvl commented Mar 3, 2019

@jeffreydwalter
Copy link

@jeffreydwalter jeffreydwalter commented Mar 25, 2019

Not yet. There is a design for it, but it requires added functionality of the core template libraries.

On Tue, 26 Feb 2019 at 13:10 Eric Cox @.***> wrote: Is there a good way to mark text in go templates for translation? — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#12750 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/AGJZR7curUH0IDM4-4BJ1TG3b4n3Swq_ks5vRWqggaJpZM4GD3-F .

Is there any ETA for this feature or a suggested work-around?

@nkall
Copy link

@nkall nkall commented Oct 18, 2019

Hi, just found this issue and cross-posting my recent proposal: #34989

Are compact number formats something which could potentially fall under the responsibilities of the x/text package, and if so, what would be the process for creating a contribution to add this functionality?

@mpvl
Copy link
Member Author

@mpvl mpvl commented Oct 19, 2019

Anything part of Unicode, including CLDR fits in the x/text mandate. You could modify the existing package to include it. The same process as with Go applies. As that is CLDR 35, it would require an upgrade to CLDR 35 of x/text first, which may take some effort.

@nkall
Copy link

@nkall nkall commented Oct 19, 2019

Great, thank you. I'll look into the difficulty of getting that upgraded. In the meantime I put together a library which serves my purpose well enough for now (for anyone who happens to stumble upon this): https://github.com/nkall/compactnumber

@jzeneto
Copy link

@jzeneto jzeneto commented Dec 12, 2019

@MickMonaghan: dates/times is on the list, but only after number etc. is completed.

Hi, since that message is from February 2017, I would like to know: date/time localization is getting closer to be implemented, or is still far in Go roadmap?

Thank you!

@Xpert85
Copy link

@Xpert85 Xpert85 commented Apr 11, 2020

Hi, the documentation of x/text is mentioning the gender feature in several places.

Do I understand correctly, that this feature is currently not implemented?

Thank you.

@mpvl
Copy link
Member Author

@mpvl mpvl commented Apr 11, 2020

@Xpert85 That is correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.