mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-23 12:18:51 +02:00
type i18n
This commit is contained in:
parent
7d9aa97f96
commit
37bb89dac3
2
go.mod
2
go.mod
@ -3,7 +3,6 @@ module github.com/jesseduffield/lazygit
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0
|
||||
github.com/atotto/clipboard v0.1.2
|
||||
github.com/aybabtme/humanlog v0.4.1
|
||||
@ -27,7 +26,6 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.3
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/onsi/gomega v1.7.1 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
|
12
go.sum
12
go.sum
@ -1,6 +1,3 @@
|
||||
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0 h1:UDLmNjCGFZZCaVMB74DqYEtXkHxnTxcr4FeJVF9uCn8=
|
||||
github.com/OpenPeeDeeP/xdg v1.0.0/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
@ -108,8 +105,6 @@ github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.3 h1:ks/JkQiOEhhuF6jpNvx+Wih1NIiXzUnZeZVnJuI8R8M=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.3/go.mod h1:oDab7q8XCYMRlcrBnaY/7B1eOectbvj6B1UPBT+p5jo=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@ -140,7 +135,6 @@ github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@ -150,16 +144,13 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw=
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -167,7 +158,6 @@ golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -180,7 +170,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -192,7 +181,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
|
2
main.go
2
main.go
@ -127,6 +127,6 @@ func main() {
|
||||
stackTrace := newErr.ErrorStack()
|
||||
app.Log.Error(stackTrace)
|
||||
|
||||
log.Fatal(fmt.Sprintf("%s\n\n%s", app.Tr.SLocalize("ErrorOccurred"), stackTrace))
|
||||
log.Fatal(fmt.Sprintf("%s\n\n%s", app.Tr.ErrorOccurred, stackTrace))
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ type App struct {
|
||||
OSCommand *oscommands.OSCommand
|
||||
GitCommand *commands.GitCommand
|
||||
Gui *gui.Gui
|
||||
Tr *i18n.Localizer
|
||||
Tr *i18n.TranslationSet
|
||||
Updater *updates.Updater // may only need this on the Gui
|
||||
ClientContext string
|
||||
}
|
||||
@ -103,7 +103,7 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
|
||||
}
|
||||
var err error
|
||||
app.Log = newLogger(config)
|
||||
app.Tr = i18n.NewLocalizer(app.Log)
|
||||
app.Tr = i18n.NewTranslationSet(app.Log)
|
||||
|
||||
// if we are being called in 'demon' mode, we can just return here
|
||||
app.ClientContext = os.Getenv("LAZYGIT_CLIENT_COMMAND")
|
||||
@ -138,7 +138,7 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
|
||||
func (app *App) validateGitVersion() error {
|
||||
output, err := app.OSCommand.RunCommandWithOutput("git --version")
|
||||
// if we get an error anywhere here we'll show the same status
|
||||
minVersionError := errors.New(app.Tr.SLocalize("minGitVersionError"))
|
||||
minVersionError := errors.New(app.Tr.MinGitVersionError)
|
||||
if err != nil {
|
||||
return minVersionError
|
||||
}
|
||||
@ -193,7 +193,7 @@ func (app *App) setupRepo() (bool, error) {
|
||||
}
|
||||
|
||||
// Offer to initialize a new repository in current directory.
|
||||
fmt.Print(app.Tr.SLocalize("CreateRepo"))
|
||||
fmt.Print(app.Tr.CreateRepo)
|
||||
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
if strings.Trim(response, " \n") != "y" {
|
||||
// check if we have a recent repo we can open
|
||||
@ -275,7 +275,7 @@ func (app *App) Close() error {
|
||||
func (app *App) KnownError(err error) (string, bool) {
|
||||
errorMessage := err.Error()
|
||||
|
||||
knownErrorMessages := []string{app.Tr.SLocalize("minGitVersionError")}
|
||||
knownErrorMessages := []string{app.Tr.MinGitVersionError}
|
||||
|
||||
for _, message := range knownErrorMessages {
|
||||
if errorMessage == message {
|
||||
@ -286,7 +286,7 @@ func (app *App) KnownError(err error) (string, bool) {
|
||||
mappings := []errorMapping{
|
||||
{
|
||||
originalError: "fatal: not a git repository",
|
||||
newError: app.Tr.SLocalize("notARepository"),
|
||||
newError: app.Tr.NotARepository,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ func NewDummyGitCommandWithOSCommand(osCommand *oscommands.OSCommand) *GitComman
|
||||
return &GitCommand{
|
||||
Log: utils.NewDummyLog(),
|
||||
OSCommand: osCommand,
|
||||
Tr: i18n.NewLocalizer(utils.NewDummyLog()),
|
||||
Tr: i18n.NewTranslationSet(utils.NewDummyLog()),
|
||||
Config: config.NewDummyAppConfig(),
|
||||
getGlobalGitConfig: func(string) (string, error) { return "", nil },
|
||||
getLocalGitConfig: func(string) (string, error) { return "", nil },
|
||||
|
@ -30,7 +30,7 @@ type GitCommand struct {
|
||||
Log *logrus.Entry
|
||||
OSCommand *oscommands.OSCommand
|
||||
Repo *gogit.Repository
|
||||
Tr *i18n.Localizer
|
||||
Tr *i18n.TranslationSet
|
||||
Config config.AppConfigurer
|
||||
getGlobalGitConfig func(string) (string, error)
|
||||
getLocalGitConfig func(string) (string, error)
|
||||
@ -44,7 +44,7 @@ type GitCommand struct {
|
||||
}
|
||||
|
||||
// NewGitCommand it runs git commands
|
||||
func NewGitCommand(log *logrus.Entry, osCommand *oscommands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer) (*GitCommand, error) {
|
||||
func NewGitCommand(log *logrus.Entry, osCommand *oscommands.OSCommand, tr *i18n.TranslationSet, config config.AppConfigurer) (*GitCommand, error) {
|
||||
var repo *gogit.Repository
|
||||
|
||||
// see what our default push behaviour is
|
||||
@ -64,7 +64,7 @@ func NewGitCommand(log *logrus.Entry, osCommand *oscommands.OSCommand, tr *i18n.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if repo, err = setupRepository(gogit.PlainOpen, tr.SLocalize); err != nil {
|
||||
if repo, err = setupRepository(gogit.PlainOpen, tr.GitconfigParseErr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ func resolvePath(path string) (string, error) {
|
||||
return filepath.EvalSymlinks(path)
|
||||
}
|
||||
|
||||
func setupRepository(openGitRepository func(string) (*gogit.Repository, error), sLocalize func(string) string) (*gogit.Repository, error) {
|
||||
func setupRepository(openGitRepository func(string) (*gogit.Repository, error), gitConfigParseErrorStr string) (*gogit.Repository, error) {
|
||||
unresolvedPath := env.GetGitDirEnv()
|
||||
if unresolvedPath == "" {
|
||||
var err error
|
||||
@ -159,7 +159,7 @@ func setupRepository(openGitRepository func(string) (*gogit.Repository, error),
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
|
||||
return nil, errors.New(sLocalize("GitconfigParseErr"))
|
||||
return nil, errors.New(gitConfigParseErrorStr)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
|
@ -159,7 +159,7 @@ func TestSetupRepository(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
openGitRepository func(string) (*gogit.Repository, error)
|
||||
sLocalize func(string) string
|
||||
errorStr string
|
||||
test func(*gogit.Repository, error)
|
||||
}
|
||||
|
||||
@ -169,9 +169,7 @@ func TestSetupRepository(t *testing.T) {
|
||||
func(string) (*gogit.Repository, error) {
|
||||
return nil, fmt.Errorf(`unquoted '\' must be followed by new line`)
|
||||
},
|
||||
func(string) string {
|
||||
return "error translated"
|
||||
},
|
||||
"error translated",
|
||||
func(r *gogit.Repository, err error) {
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "error translated")
|
||||
@ -182,7 +180,7 @@ func TestSetupRepository(t *testing.T) {
|
||||
func(string) (*gogit.Repository, error) {
|
||||
return nil, fmt.Errorf("Error from inside gogit")
|
||||
},
|
||||
func(string) string { return "" },
|
||||
"",
|
||||
func(r *gogit.Repository, err error) {
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "Error from inside gogit")
|
||||
@ -196,7 +194,7 @@ func TestSetupRepository(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
return r, nil
|
||||
},
|
||||
func(string) string { return "" },
|
||||
"",
|
||||
func(r *gogit.Repository, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, r)
|
||||
@ -206,7 +204,7 @@ func TestSetupRepository(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
s.test(setupRepository(s.openGitRepository, s.sLocalize))
|
||||
s.test(setupRepository(s.openGitRepository, s.errorStr))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -254,7 +252,7 @@ func TestNewGitCommand(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
s.setup()
|
||||
s.test(NewGitCommand(utils.NewDummyLog(), oscommands.NewDummyOSCommand(), i18n.NewLocalizer(utils.NewDummyLog()), config.NewDummyAppConfig()))
|
||||
s.test(NewGitCommand(utils.NewDummyLog(), oscommands.NewDummyOSCommand(), i18n.NewTranslationSet(utils.NewDummyLog()), config.NewDummyAppConfig()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -33,11 +33,11 @@ type CommitListBuilder struct {
|
||||
Log *logrus.Entry
|
||||
GitCommand *GitCommand
|
||||
OSCommand *oscommands.OSCommand
|
||||
Tr *i18n.Localizer
|
||||
Tr *i18n.TranslationSet
|
||||
}
|
||||
|
||||
// NewCommitListBuilder builds a new commit list builder
|
||||
func NewCommitListBuilder(log *logrus.Entry, gitCommand *GitCommand, osCommand *oscommands.OSCommand, tr *i18n.Localizer) *CommitListBuilder {
|
||||
func NewCommitListBuilder(log *logrus.Entry, gitCommand *GitCommand, osCommand *oscommands.OSCommand, tr *i18n.TranslationSet) *CommitListBuilder {
|
||||
return &CommitListBuilder{
|
||||
Log: log,
|
||||
GitCommand: gitCommand,
|
||||
@ -170,7 +170,7 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
|
||||
if rebaseMode != "" {
|
||||
currentCommit := commits[len(rebasingCommits)]
|
||||
blue := color.New(color.FgYellow)
|
||||
youAreHere := blue.Sprintf("<-- %s ---", c.Tr.SLocalize("YouAreHere"))
|
||||
youAreHere := blue.Sprintf("<-- %s ---", c.Tr.YouAreHere)
|
||||
currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ func NewDummyCommitListBuilder() *CommitListBuilder {
|
||||
Log: utils.NewDummyLog(),
|
||||
GitCommand: NewDummyGitCommandWithOSCommand(osCommand),
|
||||
OSCommand: osCommand,
|
||||
Tr: i18n.NewLocalizer(utils.NewDummyLog()),
|
||||
Tr: i18n.NewTranslationSet(utils.NewDummyLog()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ func (c *GitCommand) MovePatchToSelectedCommit(commits []*models.Commit, sourceC
|
||||
// one where we handle the possibility of a credential request, and the other
|
||||
// where we continue the rebase
|
||||
if c.usingGpg() {
|
||||
return errors.New(c.Tr.SLocalize("DisabledForGPG"))
|
||||
return errors.New(c.Tr.DisabledForGPG)
|
||||
}
|
||||
|
||||
baseIndex := sourceCommitIdx + 1
|
||||
@ -139,7 +139,7 @@ func (c *GitCommand) MovePatchToSelectedCommit(commits []*models.Commit, sourceC
|
||||
|
||||
func (c *GitCommand) PullPatchIntoIndex(commits []*models.Commit, commitIdx int, p *patch.PatchManager, stash bool) error {
|
||||
if stash {
|
||||
if err := c.StashSave(c.Tr.SLocalize("StashPrefix") + commits[commitIdx].Sha); err != nil {
|
||||
if err := c.StashSave(c.Tr.StashPrefix + commits[commitIdx].Sha); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func (pr *PullRequest) Create(branch *models.Branch) error {
|
||||
branchExistsOnRemote := pr.GitCommand.CheckRemoteBranchExists(branch)
|
||||
|
||||
if !branchExistsOnRemote {
|
||||
return errors.New(pr.GitCommand.Tr.SLocalize("NoBranchOnRemote"))
|
||||
return errors.New(pr.GitCommand.Tr.NoBranchOnRemote)
|
||||
}
|
||||
|
||||
repoURL := pr.GitCommand.GetRemoteURL()
|
||||
@ -108,7 +108,7 @@ func (pr *PullRequest) Create(branch *models.Branch) error {
|
||||
}
|
||||
|
||||
if gitService == nil {
|
||||
return errors.New(pr.GitCommand.Tr.SLocalize("UnsupportedGitService"))
|
||||
return errors.New(pr.GitCommand.Tr.UnsupportedGitService)
|
||||
}
|
||||
|
||||
repoInfo := getRepoInfoFromURL(repoURL)
|
||||
|
@ -26,7 +26,7 @@ func (c *GitCommand) MoveCommitDown(commits []*models.Commit, index int) error {
|
||||
// we must ensure that we have at least two commits after the selected one
|
||||
if len(commits) <= index+2 {
|
||||
// assuming they aren't picking the bottom commit
|
||||
return errors.New(c.Tr.SLocalize("NoRoom"))
|
||||
return errors.New(c.Tr.NoRoom)
|
||||
}
|
||||
|
||||
todo := ""
|
||||
@ -101,14 +101,14 @@ func (c *GitCommand) GenerateGenericRebaseTodo(commits []*models.Commit, actionI
|
||||
baseIndex := actionIndex + 1
|
||||
|
||||
if len(commits) <= baseIndex {
|
||||
return "", "", errors.New(c.Tr.SLocalize("CannotRebaseOntoFirstCommit"))
|
||||
return "", "", errors.New(c.Tr.CannotRebaseOntoFirstCommit)
|
||||
}
|
||||
|
||||
if action == "squash" || action == "fixup" {
|
||||
baseIndex++
|
||||
|
||||
if len(commits) <= baseIndex {
|
||||
return "", "", errors.New(c.Tr.SLocalize("CannotSquashOntoSecondCommit"))
|
||||
return "", "", errors.New(c.Tr.CannotSquashOntoSecondCommit)
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +212,7 @@ func (c *GitCommand) BeginInteractiveRebaseForCommit(commits []*models.Commit, c
|
||||
// one where we handle the possibility of a credential request, and the other
|
||||
// where we continue the rebase
|
||||
if c.usingGpg() {
|
||||
return errors.New(c.Tr.SLocalize("DisabledForGPG"))
|
||||
return errors.New(c.Tr.DisabledForGPG)
|
||||
}
|
||||
|
||||
todo, sha, err := c.GenerateGenericRebaseTodo(commits, commitIndex, "edit")
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -28,7 +29,7 @@ func (gui *Gui) handleBranchSelect() error {
|
||||
var task updateTask
|
||||
branch := gui.getSelectedBranch()
|
||||
if branch == nil {
|
||||
task = gui.createRenderStringTask(gui.Tr.SLocalize("NoBranchesThisRepo"))
|
||||
task = gui.createRenderStringTask(gui.Tr.NoBranchesThisRepo)
|
||||
} else {
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
|
||||
@ -81,7 +82,7 @@ func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
|
||||
return gui.createErrorPanel(gui.Tr.AlreadyCheckedOutBranch)
|
||||
}
|
||||
branch := gui.getSelectedBranch()
|
||||
return gui.handleCheckoutRef(branch.Name, handleCheckoutRefOptions{})
|
||||
@ -99,7 +100,7 @@ func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error
|
||||
}
|
||||
|
||||
func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := gui.createLoaderPanel(v, gui.Tr.SLocalize("FetchWait")); err != nil {
|
||||
if err := gui.createLoaderPanel(v, gui.Tr.FetchWait); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
@ -112,8 +113,8 @@ func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
|
||||
branch := gui.getSelectedBranch()
|
||||
message := gui.Tr.SLocalize("SureForceCheckout")
|
||||
title := gui.Tr.SLocalize("ForceCheckoutBranch")
|
||||
message := gui.Tr.SureForceCheckout
|
||||
title := gui.Tr.ForceCheckoutBranch
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: title,
|
||||
@ -136,7 +137,7 @@ type handleCheckoutRefOptions struct {
|
||||
func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions) error {
|
||||
waitingStatus := options.WaitingStatus
|
||||
if waitingStatus == "" {
|
||||
waitingStatus = gui.Tr.SLocalize("CheckingOutStatus")
|
||||
waitingStatus = gui.Tr.CheckingOutStatus
|
||||
}
|
||||
|
||||
cmdOptions := commands.CheckoutOptions{Force: false, EnvVars: options.EnvVars}
|
||||
@ -160,10 +161,10 @@ func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions)
|
||||
// offer to autostash changes
|
||||
return gui.ask(askOpts{
|
||||
|
||||
title: gui.Tr.SLocalize("AutoStashTitle"),
|
||||
prompt: gui.Tr.SLocalize("AutoStashPrompt"),
|
||||
title: gui.Tr.AutoStashTitle,
|
||||
prompt: gui.Tr.AutoStashPrompt,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + ref); err != nil {
|
||||
if err := gui.GitCommand.StashSave(gui.Tr.StashPrefix + ref); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
if err := gui.GitCommand.Checkout(ref, cmdOptions); err != nil {
|
||||
@ -193,14 +194,14 @@ func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.prompt(gui.Tr.SLocalize("BranchName")+":", "", func(response string) error {
|
||||
return gui.prompt(gui.Tr.BranchName+":", "", func(response string) error {
|
||||
return gui.handleCheckoutRef(response, handleCheckoutRefOptions{
|
||||
onRefNotFound: func(ref string) error {
|
||||
|
||||
return gui.ask(askOpts{
|
||||
|
||||
title: gui.Tr.SLocalize("BranchNotFoundTitle"),
|
||||
prompt: fmt.Sprintf("%s %s%s", gui.Tr.SLocalize("BranchNotFoundPrompt"), ref, "?"),
|
||||
title: gui.Tr.BranchNotFoundTitle,
|
||||
prompt: fmt.Sprintf("%s %s%s", gui.Tr.BranchNotFoundPrompt, ref, "?"),
|
||||
handleConfirm: func() error {
|
||||
return gui.createNewBranchWithName(ref)
|
||||
},
|
||||
@ -243,22 +244,22 @@ func (gui *Gui) deleteBranch(force bool) error {
|
||||
}
|
||||
checkedOutBranch := gui.getCheckedOutBranch()
|
||||
if checkedOutBranch.Name == selectedBranch.Name {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("CantDeleteCheckOutBranch"))
|
||||
return gui.createErrorPanel(gui.Tr.CantDeleteCheckOutBranch)
|
||||
}
|
||||
return gui.deleteNamedBranch(selectedBranch, force)
|
||||
}
|
||||
|
||||
func (gui *Gui) deleteNamedBranch(selectedBranch *models.Branch, force bool) error {
|
||||
title := gui.Tr.SLocalize("DeleteBranch")
|
||||
var messageID string
|
||||
title := gui.Tr.DeleteBranch
|
||||
var templateStr string
|
||||
if force {
|
||||
messageID = "ForceDeleteBranchMessage"
|
||||
templateStr = gui.Tr.ForceDeleteBranchMessage
|
||||
} else {
|
||||
messageID = "DeleteBranchMessage"
|
||||
templateStr = gui.Tr.DeleteBranchMessage
|
||||
}
|
||||
message := gui.Tr.TemplateLocalize(
|
||||
messageID,
|
||||
Teml{
|
||||
message := utils.ResolvePlaceholderString(
|
||||
templateStr,
|
||||
map[string]string{
|
||||
"selectedBranchName": selectedBranch.Name,
|
||||
},
|
||||
)
|
||||
@ -290,11 +291,11 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
|
||||
}
|
||||
checkedOutBranchName := gui.getCheckedOutBranch().Name
|
||||
if checkedOutBranchName == branchName {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("CantMergeBranchIntoItself"))
|
||||
return gui.createErrorPanel(gui.Tr.CantMergeBranchIntoItself)
|
||||
}
|
||||
prompt := gui.Tr.TemplateLocalize(
|
||||
"ConfirmMerge",
|
||||
Teml{
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
gui.Tr.ConfirmMerge,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
"selectedBranch": branchName,
|
||||
},
|
||||
@ -302,7 +303,7 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
|
||||
|
||||
return gui.ask(askOpts{
|
||||
|
||||
title: gui.Tr.SLocalize("MergingTitle"),
|
||||
title: gui.Tr.MergingTitle,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
err := gui.GitCommand.Merge(branchName, commands.MergeOpts{})
|
||||
@ -332,11 +333,11 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
||||
|
||||
checkedOutBranch := gui.getCheckedOutBranch().Name
|
||||
if selectedBranchName == checkedOutBranch {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("CantRebaseOntoSelf"))
|
||||
return gui.createErrorPanel(gui.Tr.CantRebaseOntoSelf)
|
||||
}
|
||||
prompt := gui.Tr.TemplateLocalize(
|
||||
"ConfirmRebase",
|
||||
Teml{
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
gui.Tr.ConfirmRebase,
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranch,
|
||||
"selectedBranch": selectedBranchName,
|
||||
},
|
||||
@ -344,7 +345,7 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
||||
|
||||
return gui.ask(askOpts{
|
||||
|
||||
title: gui.Tr.SLocalize("RebasingTitle"),
|
||||
title: gui.Tr.RebasingTitle,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
err := gui.GitCommand.RebaseBranch(selectedBranchName)
|
||||
@ -362,10 +363,10 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
if branch.Pushables == "?" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("FwdNoUpstream"))
|
||||
return gui.createErrorPanel(gui.Tr.FwdNoUpstream)
|
||||
}
|
||||
if branch.Pushables != "0" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("FwdCommitsToPush"))
|
||||
return gui.createErrorPanel(gui.Tr.FwdCommitsToPush)
|
||||
}
|
||||
|
||||
upstream, err := gui.GitCommand.GetUpstreamForBranch(branch.Name)
|
||||
@ -377,9 +378,9 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
|
||||
remoteName := split[0]
|
||||
remoteBranchName := strings.Join(split[1:], "/")
|
||||
|
||||
message := gui.Tr.TemplateLocalize(
|
||||
"Fetching",
|
||||
Teml{
|
||||
message := utils.ResolvePlaceholderString(
|
||||
gui.Tr.Fetching,
|
||||
map[string]string{
|
||||
"from": fmt.Sprintf("%s/%s", remoteName, remoteBranchName),
|
||||
"to": branch.Name,
|
||||
},
|
||||
@ -417,7 +418,7 @@ func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error {
|
||||
// way to get it to show up in the reflog)
|
||||
|
||||
promptForNewName := func() error {
|
||||
return gui.prompt(gui.Tr.SLocalize("NewBranchNamePrompt")+" "+branch.Name+":", "", func(newBranchName string) error {
|
||||
return gui.prompt(gui.Tr.NewBranchNamePrompt+" "+branch.Name+":", "", func(newBranchName string) error {
|
||||
if err := gui.GitCommand.RenameBranch(branch.Name, newBranchName); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@ -441,8 +442,8 @@ func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
return gui.ask(askOpts{
|
||||
|
||||
title: gui.Tr.SLocalize("renameBranch"),
|
||||
prompt: gui.Tr.SLocalize("RenameBranchWarning"),
|
||||
title: gui.Tr.LcRenameBranch,
|
||||
prompt: gui.Tr.RenameBranchWarning,
|
||||
handleConfirm: promptForNewName,
|
||||
})
|
||||
}
|
||||
@ -462,9 +463,9 @@ func (gui *Gui) handleNewBranchOffCurrentItem() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
message := gui.Tr.TemplateLocalize(
|
||||
"NewBranchNameBranchOff",
|
||||
Teml{
|
||||
message := utils.ResolvePlaceholderString(
|
||||
gui.Tr.NewBranchNameBranchOff,
|
||||
map[string]string{
|
||||
"branchName": item.Description(),
|
||||
},
|
||||
)
|
||||
|
@ -144,10 +144,10 @@ func (gui *Gui) HandlePasteCommits() error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("CherryPick"),
|
||||
prompt: gui.Tr.SLocalize("SureCherryPick"),
|
||||
title: gui.Tr.CherryPick,
|
||||
prompt: gui.Tr.SureCherryPick,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("CherryPickingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.CherryPickingStatus, func() error {
|
||||
err := gui.GitCommand.CherryPickCommits(gui.State.Modes.CherryPicking.CherryPickedCommits)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
})
|
||||
|
@ -60,10 +60,10 @@ func (gui *Gui) handleDiscardOldFileChange(g *gocui.Gui, v *gocui.View) error {
|
||||
fileName := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLineIdx].Name
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("DiscardFileChangesTitle"),
|
||||
prompt: gui.Tr.SLocalize("DiscardFileChangesPrompt"),
|
||||
title: gui.Tr.DiscardFileChangesTitle,
|
||||
prompt: gui.Tr.DiscardFileChangesPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
if err := gui.GitCommand.DiscardOldFileChanges(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, fileName); err != nil {
|
||||
if err := gui.handleGenericMergeCommandResult(err); err != nil {
|
||||
return err
|
||||
@ -137,8 +137,8 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("DiscardPatch"),
|
||||
prompt: gui.Tr.SLocalize("DiscardPatchConfirm"),
|
||||
title: gui.Tr.DiscardPatch,
|
||||
prompt: gui.Tr.DiscardPatchConfirm,
|
||||
handleConfirm: func() error {
|
||||
gui.GitCommand.PatchManager.Reset()
|
||||
return toggleTheFile()
|
||||
@ -184,8 +184,8 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
|
||||
|
||||
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("DiscardPatch"),
|
||||
prompt: gui.Tr.SLocalize("DiscardPatchConfirm"),
|
||||
title: gui.Tr.DiscardPatch,
|
||||
prompt: gui.Tr.DiscardPatchConfirm,
|
||||
handlersManageFocus: true,
|
||||
handleConfirm: func() error {
|
||||
gui.GitCommand.PatchManager.Reset()
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// runSyncOrAsyncCommand takes the output of a command that may have returned
|
||||
@ -28,7 +29,7 @@ func (gui *Gui) runSyncOrAsyncCommand(sub *exec.Cmd, err error) (bool, error) {
|
||||
func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
|
||||
message := gui.trimmedContent(v)
|
||||
if message == "" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("CommitWithoutMessageErr"))
|
||||
return gui.createErrorPanel(gui.Tr.CommitWithoutMessageErr)
|
||||
}
|
||||
flags := ""
|
||||
skipHookPrefix := gui.Config.GetUserConfig().Git.SkipHookPrefix
|
||||
@ -53,14 +54,15 @@ func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCommitMessageFocused() error {
|
||||
message := gui.Tr.TemplateLocalize(
|
||||
"CommitMessageConfirm",
|
||||
Teml{
|
||||
message := utils.ResolvePlaceholderString(
|
||||
gui.Tr.CommitMessageConfirm,
|
||||
map[string]string{
|
||||
"keyBindClose": "esc",
|
||||
"keyBindConfirm": "enter",
|
||||
"keyBindNewLine": "tab",
|
||||
},
|
||||
)
|
||||
|
||||
gui.renderString("options", message)
|
||||
return nil
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -35,7 +36,7 @@ func (gui *Gui) handleCommitSelect() error {
|
||||
var task updateTask
|
||||
commit := gui.getSelectedLocalCommit()
|
||||
if commit == nil {
|
||||
task = gui.createRenderStringTask(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||
task = gui.createRenderStringTask(gui.Tr.NoCommitsThisBranch)
|
||||
} else {
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.Path),
|
||||
@ -153,7 +154,7 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
if len(gui.State.Commits) <= 1 {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
||||
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
||||
}
|
||||
|
||||
applied, err := gui.handleMidRebaseCommand("squash")
|
||||
@ -165,10 +166,10 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("Squash"),
|
||||
prompt: gui.Tr.SLocalize("SureSquashThisCommit"),
|
||||
title: gui.Tr.Squash,
|
||||
prompt: gui.Tr.SureSquashThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "squash")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
})
|
||||
@ -182,7 +183,7 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
if len(gui.State.Commits) <= 1 {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
||||
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
||||
}
|
||||
|
||||
applied, err := gui.handleMidRebaseCommand("fixup")
|
||||
@ -194,10 +195,10 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("Fixup"),
|
||||
prompt: gui.Tr.SLocalize("SureFixupThisCommit"),
|
||||
title: gui.Tr.Fixup,
|
||||
prompt: gui.Tr.SureFixupThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("FixingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.FixingStatus, func() error {
|
||||
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "fixup")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
})
|
||||
@ -219,7 +220,7 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
if gui.State.Panels.Commits.SelectedLineIdx != 0 {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
||||
return gui.createErrorPanel(gui.Tr.OnlyRenameTopCommit)
|
||||
}
|
||||
|
||||
commit := gui.getSelectedLocalCommit()
|
||||
@ -232,7 +233,7 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.prompt(gui.Tr.SLocalize("renameCommit"), message, func(response string) error {
|
||||
return gui.prompt(gui.Tr.LcRenameCommit, message, func(response string) error {
|
||||
if err := gui.GitCommand.RenameCommit(response); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@ -280,7 +281,7 @@ func (gui *Gui) handleMidRebaseCommand(action string) (bool, error) {
|
||||
// our input or we set a lazygit client as the EDITOR env variable and have it
|
||||
// request us to edit the commit message when prompted.
|
||||
if action == "reword" {
|
||||
return true, gui.createErrorPanel(gui.Tr.SLocalize("rewordNotSupported"))
|
||||
return true, gui.createErrorPanel(gui.Tr.LcRewordNotSupported)
|
||||
}
|
||||
|
||||
if err := gui.GitCommand.EditRebaseTodo(gui.State.Panels.Commits.SelectedLineIdx, action); err != nil {
|
||||
@ -304,10 +305,10 @@ func (gui *Gui) handleCommitDelete(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("DeleteCommitTitle"),
|
||||
prompt: gui.Tr.SLocalize("DeleteCommitPrompt"),
|
||||
title: gui.Tr.DeleteCommitTitle,
|
||||
prompt: gui.Tr.DeleteCommitPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("DeletingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "drop")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
})
|
||||
@ -333,7 +334,7 @@ func (gui *Gui) handleCommitMoveDown(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.refreshRebaseCommits()
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("MovingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.MovingStatus, func() error {
|
||||
err := gui.GitCommand.MoveCommitDown(gui.State.Commits, index)
|
||||
if err == nil {
|
||||
gui.State.Panels.Commits.SelectedLineIdx++
|
||||
@ -360,7 +361,7 @@ func (gui *Gui) handleCommitMoveUp(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.refreshRebaseCommits()
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("MovingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.MovingStatus, func() error {
|
||||
err := gui.GitCommand.MoveCommitDown(gui.State.Commits, index-1)
|
||||
if err == nil {
|
||||
gui.State.Panels.Commits.SelectedLineIdx--
|
||||
@ -382,7 +383,7 @@ func (gui *Gui) handleCommitEdit(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
err = gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "edit")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
})
|
||||
@ -394,10 +395,10 @@ func (gui *Gui) handleCommitAmendTo(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("AmendCommitTitle"),
|
||||
prompt: gui.Tr.SLocalize("AmendCommitPrompt"),
|
||||
title: gui.Tr.AmendCommitTitle,
|
||||
prompt: gui.Tr.AmendCommitPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("AmendingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.AmendingStatus, func() error {
|
||||
err := gui.GitCommand.AmendTo(gui.State.Commits[gui.State.Panels.Commits.SelectedLineIdx].Sha)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
})
|
||||
@ -467,14 +468,16 @@ func (gui *Gui) handleCreateFixupCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
gui.Tr.SureCreateFixupCommit,
|
||||
map[string]string{
|
||||
"commit": commit.Sha,
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("CreateFixupCommit"),
|
||||
prompt: gui.Tr.TemplateLocalize(
|
||||
"SureCreateFixupCommit",
|
||||
Teml{
|
||||
"commit": commit.Sha,
|
||||
},
|
||||
),
|
||||
title: gui.Tr.CreateFixupCommit,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.CreateFixupCommit(commit.Sha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
@ -495,16 +498,18 @@ func (gui *Gui) handleSquashAllAboveFixupCommits(g *gocui.Gui, v *gocui.View) er
|
||||
return nil
|
||||
}
|
||||
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
gui.Tr.SureSquashAboveCommits,
|
||||
map[string]string{
|
||||
"commit": commit.Sha,
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("SquashAboveCommits"),
|
||||
prompt: gui.Tr.TemplateLocalize(
|
||||
"SureSquashAboveCommits",
|
||||
Teml{
|
||||
"commit": commit.Sha,
|
||||
},
|
||||
),
|
||||
title: gui.Tr.SquashAboveCommits,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||
err := gui.GitCommand.SquashAllAboveFixupCommits(commit.Sha)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
})
|
||||
@ -525,7 +530,7 @@ func (gui *Gui) handleTagCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
|
||||
return gui.prompt(gui.Tr.SLocalize("TagNameTitle"), "", func(response string) error {
|
||||
return gui.prompt(gui.Tr.TagNameTitle, "", func(response string) error {
|
||||
if err := gui.GitCommand.CreateLightweightTag(response, commitSha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@ -540,8 +545,8 @@ func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("checkoutCommit"),
|
||||
prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"),
|
||||
title: gui.Tr.LcCheckoutCommit,
|
||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||
},
|
||||
@ -551,7 +556,7 @@ func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
func (gui *Gui) handleCreateCommitResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
commit := gui.getSelectedLocalCommit()
|
||||
if commit == nil {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||
return gui.createErrorPanel(gui.Tr.NoCommitsThisBranch)
|
||||
}
|
||||
|
||||
return gui.createResetMenu(commit.Sha)
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type createPopupPanelOpts struct {
|
||||
@ -205,9 +206,9 @@ func (gui *Gui) createPopupPanel(opts createPopupPanelOpts) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||
actions := gui.Tr.TemplateLocalize(
|
||||
"CloseConfirm",
|
||||
Teml{
|
||||
actions := utils.ResolvePlaceholderString(
|
||||
gui.Tr.CloseConfirm,
|
||||
map[string]string{
|
||||
"keyBindClose": "esc",
|
||||
"keyBindConfirm": "enter",
|
||||
},
|
||||
@ -240,7 +241,7 @@ func (gui *Gui) createErrorPanel(message string) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("Error"),
|
||||
title: gui.Tr.Error,
|
||||
prompt: coloredMessage,
|
||||
})
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type credentials chan string
|
||||
@ -14,10 +15,10 @@ func (gui *Gui) promptUserForCredential(passOrUname string) string {
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
credentialsView, _ := g.View("credentials")
|
||||
if passOrUname == "username" {
|
||||
credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername")
|
||||
credentialsView.Title = gui.Tr.CredentialsUsername
|
||||
credentialsView.Mask = 0
|
||||
} else {
|
||||
credentialsView.Title = gui.Tr.SLocalize("CredentialsPassword")
|
||||
credentialsView.Title = gui.Tr.CredentialsPassword
|
||||
credentialsView.Mask = '*'
|
||||
}
|
||||
|
||||
@ -53,13 +54,14 @@ func (gui *Gui) handleCloseCredentialsView(g *gocui.Gui, v *gocui.View) error {
|
||||
func (gui *Gui) handleCredentialsViewFocused() error {
|
||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||
|
||||
message := gui.Tr.TemplateLocalize(
|
||||
"CloseConfirm",
|
||||
Teml{
|
||||
message := utils.ResolvePlaceholderString(
|
||||
gui.Tr.CloseConfirm,
|
||||
map[string]string{
|
||||
"keyBindClose": gui.getKeyDisplay(keybindingConfig.Universal.Return),
|
||||
"keyBindConfirm": gui.getKeyDisplay(keybindingConfig.Universal.Confirm),
|
||||
},
|
||||
)
|
||||
|
||||
gui.renderString("options", message)
|
||||
return nil
|
||||
}
|
||||
@ -69,7 +71,7 @@ func (gui *Gui) handleCredentialsPopup(cmdErr error) {
|
||||
if cmdErr != nil {
|
||||
errMessage := cmdErr.Error()
|
||||
if strings.Contains(errMessage, "Invalid username or password") {
|
||||
errMessage = gui.Tr.SLocalize("PassUnameWrong")
|
||||
errMessage = gui.Tr.PassUnameWrong
|
||||
}
|
||||
// we are not logging this error because it may contain a password
|
||||
gui.createErrorPanel(errMessage)
|
||||
|
@ -62,7 +62,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
|
||||
loadingText := customCommand.LoadingText
|
||||
if loadingText == "" {
|
||||
loadingText = gui.Tr.SLocalize("runningCustomCommandStatus")
|
||||
loadingText = gui.Tr.LcRunningCustomCommandStatus
|
||||
}
|
||||
return gui.WithWaitingStatus(loadingText, func() error {
|
||||
gui.OSCommand.PrepareSubProcess(cmdStr)
|
||||
|
@ -114,7 +114,7 @@ func (gui *Gui) handleCreateDiffingMenuPanel(g *gocui.Gui, v *gocui.View) error
|
||||
name := name
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
{
|
||||
displayString: fmt.Sprintf("%s %s", gui.Tr.SLocalize("diff"), name),
|
||||
displayString: fmt.Sprintf("%s %s", gui.Tr.LcDiff, name),
|
||||
onPress: func() error {
|
||||
gui.State.Modes.Diffing.Ref = name
|
||||
// can scope this down based on current view but too lazy right now
|
||||
@ -126,9 +126,9 @@ func (gui *Gui) handleCreateDiffingMenuPanel(g *gocui.Gui, v *gocui.View) error
|
||||
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("enterRefToDiff"),
|
||||
displayString: gui.Tr.LcEnterRefToDiff,
|
||||
onPress: func() error {
|
||||
return gui.prompt(gui.Tr.SLocalize("enteRefName"), "", func(response string) error {
|
||||
return gui.prompt(gui.Tr.LcEnteRefName, "", func(response string) error {
|
||||
gui.State.Modes.Diffing.Ref = strings.TrimSpace(response)
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
})
|
||||
@ -139,14 +139,14 @@ func (gui *Gui) handleCreateDiffingMenuPanel(g *gocui.Gui, v *gocui.View) error
|
||||
if gui.State.Modes.Diffing.Active() {
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("swapDiff"),
|
||||
displayString: gui.Tr.LcSwapDiff,
|
||||
onPress: func() error {
|
||||
gui.State.Modes.Diffing.Reverse = !gui.State.Modes.Diffing.Reverse
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("exitDiffMode"),
|
||||
displayString: gui.Tr.LcExitDiffMode,
|
||||
onPress: func() error {
|
||||
gui.State.Modes.Diffing = Diffing{}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
@ -155,5 +155,5 @@ func (gui *Gui) handleCreateDiffingMenuPanel(g *gocui.Gui, v *gocui.View) error
|
||||
}...)
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SLocalize("DiffingMenuTitle"), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.createMenu(gui.Tr.DiffingMenuTitle, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
menuItems = []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("submoduleStashAndReset"),
|
||||
displayString: gui.Tr.LcSubmoduleStashAndReset,
|
||||
onPress: func() error {
|
||||
return gui.resetSubmodule(submodule)
|
||||
},
|
||||
@ -38,7 +38,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
} else {
|
||||
menuItems = []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("discardAllChanges"),
|
||||
displayString: gui.Tr.LcDiscardAllChanges,
|
||||
onPress: func() error {
|
||||
if err := gui.GitCommand.DiscardAllFileChanges(file); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
@ -50,7 +50,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
if file.HasStagedChanges && file.HasUnstagedChanges {
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: gui.Tr.SLocalize("discardUnstagedChanges"),
|
||||
displayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||
onPress: func() error {
|
||||
if err := gui.GitCommand.DiscardUnstagedFileChanges(file); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
|
@ -38,7 +38,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
main: &viewUpdateOpts{
|
||||
title: "",
|
||||
task: gui.createRenderStringTask(gui.Tr.SLocalize("NoChangedFiles")),
|
||||
task: gui.createRenderStringTask(gui.Tr.NoChangedFiles),
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -61,7 +61,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
|
||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||
title: gui.Tr.SLocalize("UnstagedChanges"),
|
||||
title: gui.Tr.UnstagedChanges,
|
||||
task: gui.createRunPtyTask(cmd),
|
||||
}}
|
||||
|
||||
@ -70,11 +70,11 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
|
||||
refreshOpts.secondary = &viewUpdateOpts{
|
||||
title: gui.Tr.SLocalize("StagedChanges"),
|
||||
title: gui.Tr.StagedChanges,
|
||||
task: gui.createRunPtyTask(cmd),
|
||||
}
|
||||
} else if !file.HasUnstagedChanges {
|
||||
refreshOpts.main.title = gui.Tr.SLocalize("StagedChanges")
|
||||
refreshOpts.main.title = gui.Tr.StagedChanges
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshOpts)
|
||||
@ -181,7 +181,7 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error
|
||||
return gui.handleSwitchToMerge()
|
||||
}
|
||||
if file.HasMergeConflicts {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("FileStagingRequirements"))
|
||||
return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
|
||||
}
|
||||
gui.switchContext(gui.Contexts.Staging.Context)
|
||||
|
||||
@ -257,8 +257,8 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
if file.Tracked {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("IgnoreTracked"),
|
||||
prompt: gui.Tr.SLocalize("IgnoreTrackedPrompt"),
|
||||
title: gui.Tr.IgnoreTracked,
|
||||
prompt: gui.Tr.IgnoreTrackedPrompt,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.Ignore(file.Name); err != nil {
|
||||
return err
|
||||
@ -281,7 +281,7 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
||||
func (gui *Gui) handleWIPCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
||||
skipHookPreifx := gui.Config.GetUserConfig().Git.SkipHookPrefix
|
||||
if skipHookPreifx == "" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("SkipHookPrefixNotConfigured"))
|
||||
return gui.createErrorPanel(gui.Tr.SkipHookPrefixNotConfigured)
|
||||
}
|
||||
|
||||
gui.renderStringSync("commitMessage", skipHookPreifx)
|
||||
@ -315,7 +315,7 @@ func (gui *Gui) handleCommitPress() error {
|
||||
prefixReplace := commitPrefixConfig.Replace
|
||||
rgx, err := regexp.Compile(prefixPattern)
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(fmt.Sprintf("%s: %s", gui.Tr.SLocalize("commitPrefixPatternError"), err.Error()))
|
||||
return gui.createErrorPanel(fmt.Sprintf("%s: %s", gui.Tr.LcCommitPrefixPatternError, err.Error()))
|
||||
}
|
||||
prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace)
|
||||
gui.renderString("commitMessage", prefix)
|
||||
@ -337,8 +337,8 @@ func (gui *Gui) handleCommitPress() error {
|
||||
|
||||
func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("NoFilesStagedTitle"),
|
||||
prompt: gui.Tr.SLocalize("NoFilesStagedPrompt"),
|
||||
title: gui.Tr.NoFilesStagedTitle,
|
||||
prompt: gui.Tr.NoFilesStagedPrompt,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.StageAll(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
@ -360,14 +360,14 @@ func (gui *Gui) handleAmendCommitPress() error {
|
||||
}
|
||||
|
||||
if len(gui.State.Commits) == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("NoCommitToAmend"))
|
||||
return gui.createErrorPanel(gui.Tr.NoCommitToAmend)
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: strings.Title(gui.Tr.SLocalize("AmendLastCommit")),
|
||||
prompt: gui.Tr.SLocalize("SureToAmend"),
|
||||
title: strings.Title(gui.Tr.AmendLastCommit),
|
||||
prompt: gui.Tr.SureToAmend,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("AmendingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.AmendingStatus, func() error {
|
||||
ok, err := gui.runSyncOrAsyncCommand(gui.GitCommand.AmendHead())
|
||||
if err != nil {
|
||||
return err
|
||||
@ -484,7 +484,7 @@ func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.prompt(gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranch.Name, func(upstream string) error {
|
||||
return gui.prompt(gui.Tr.EnterUpstream, "origin/"+currentBranch.Name, func(upstream string) error {
|
||||
if err := gui.GitCommand.SetUpstreamBranch(upstream); err != nil {
|
||||
errorMessage := err.Error()
|
||||
if strings.Contains(errorMessage, "does not exist") {
|
||||
@ -505,7 +505,7 @@ type PullFilesOptions struct {
|
||||
}
|
||||
|
||||
func (gui *Gui) pullFiles(opts PullFilesOptions) error {
|
||||
if err := gui.createLoaderPanel(gui.g.CurrentView(), gui.Tr.SLocalize("PullWait")); err != nil {
|
||||
if err := gui.createLoaderPanel(gui.g.CurrentView(), gui.Tr.PullWait); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -548,7 +548,7 @@ func (gui *Gui) pullWithMode(mode string, opts PullFilesOptions) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) pushWithForceFlag(v *gocui.View, force bool, upstream string, args string) error {
|
||||
if err := gui.createLoaderPanel(v, gui.Tr.SLocalize("PushWait")); err != nil {
|
||||
if err := gui.createLoaderPanel(v, gui.Tr.PushWait); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
@ -557,12 +557,12 @@ func (gui *Gui) pushWithForceFlag(v *gocui.View, force bool, upstream string, ar
|
||||
if err != nil && !force && strings.Contains(err.Error(), "Updates were rejected") {
|
||||
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
||||
if forcePushDisabled {
|
||||
gui.createErrorPanel(gui.Tr.SLocalize("UpdatesRejectedAndForcePushDisabled"))
|
||||
gui.createErrorPanel(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
||||
return
|
||||
}
|
||||
gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("ForcePush"),
|
||||
prompt: gui.Tr.SLocalize("ForcePushPrompt"),
|
||||
title: gui.Tr.ForcePush,
|
||||
prompt: gui.Tr.ForcePushPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.pushWithForceFlag(v, true, upstream, args)
|
||||
},
|
||||
@ -598,7 +598,7 @@ func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
||||
if gui.GitCommand.PushToCurrent {
|
||||
return gui.pushWithForceFlag(v, false, "", "--set-upstream")
|
||||
} else {
|
||||
return gui.prompt(gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranch.Name, func(response string) error {
|
||||
return gui.prompt(gui.Tr.EnterUpstream, "origin "+currentBranch.Name, func(response string) error {
|
||||
return gui.pushWithForceFlag(v, false, response, "")
|
||||
})
|
||||
}
|
||||
@ -608,12 +608,12 @@ func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
||||
if forcePushDisabled {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("ForcePushDisabled"))
|
||||
return gui.createErrorPanel(gui.Tr.ForcePushDisabled)
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("ForcePush"),
|
||||
prompt: gui.Tr.SLocalize("ForcePushPrompt"),
|
||||
title: gui.Tr.ForcePush,
|
||||
prompt: gui.Tr.ForcePushPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.pushWithForceFlag(v, true, "", "")
|
||||
},
|
||||
@ -627,7 +627,7 @@ func (gui *Gui) handleSwitchToMerge() error {
|
||||
}
|
||||
|
||||
if !file.HasInlineMergeConflicts {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("FileNoMergeCons"))
|
||||
return gui.createErrorPanel(gui.Tr.FileNoMergeCons)
|
||||
}
|
||||
|
||||
return gui.switchContext(gui.Contexts.Merging.Context)
|
||||
@ -650,7 +650,7 @@ func (gui *Gui) anyFilesWithMergeConflicts() bool {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.prompt(gui.Tr.SLocalize("CustomCommand"), "", func(command string) error {
|
||||
return gui.prompt(gui.Tr.CustomCommand, "", func(command string) error {
|
||||
gui.SubProcess = gui.OSCommand.RunCustomCommand(command)
|
||||
return gui.Errors.ErrSubProcess
|
||||
})
|
||||
@ -659,20 +659,20 @@ func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
func (gui *Gui) handleCreateStashMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("stashAllChanges"),
|
||||
displayString: gui.Tr.LcStashAllChanges,
|
||||
onPress: func() error {
|
||||
return gui.handleStashSave(gui.GitCommand.StashSave)
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("stashStagedChanges"),
|
||||
displayString: gui.Tr.LcStashStagedChanges,
|
||||
onPress: func() error {
|
||||
return gui.handleStashSave(gui.GitCommand.StashSaveStagedChanges)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SLocalize("stashOptions"), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.createMenu(gui.Tr.LcStashOptions, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStashChanges(g *gocui.Gui, v *gocui.View) error {
|
||||
|
@ -3,8 +3,8 @@ package gui
|
||||
func (gui *Gui) validateNotInFilterMode() (bool, error) {
|
||||
if gui.State.Modes.Filtering.Active() {
|
||||
err := gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("MustExitFilterModeTitle"),
|
||||
prompt: gui.Tr.SLocalize("MustExitFilterModePrompt"),
|
||||
title: gui.Tr.MustExitFilterModeTitle,
|
||||
prompt: gui.Tr.MustExitFilterModePrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.exitFilterMode()
|
||||
},
|
||||
|
@ -30,7 +30,7 @@ func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) erro
|
||||
|
||||
if fileName != "" {
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: fmt.Sprintf("%s '%s'", gui.Tr.SLocalize("filterBy"), fileName),
|
||||
displayString: fmt.Sprintf("%s '%s'", gui.Tr.LcFilterBy, fileName),
|
||||
onPress: func() error {
|
||||
gui.State.Modes.Filtering.Path = fileName
|
||||
return gui.Errors.ErrRestart
|
||||
@ -39,9 +39,9 @@ func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) erro
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: gui.Tr.SLocalize("filterPathOption"),
|
||||
displayString: gui.Tr.LcFilterPathOption,
|
||||
onPress: func() error {
|
||||
return gui.prompt(gui.Tr.SLocalize("enterFileName"), "", func(response string) error {
|
||||
return gui.prompt(gui.Tr.LcEnterFileName, "", func(response string) error {
|
||||
gui.State.Modes.Filtering.Path = strings.TrimSpace(response)
|
||||
return gui.Errors.ErrRestart
|
||||
})
|
||||
@ -50,7 +50,7 @@ func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) erro
|
||||
|
||||
if gui.State.Modes.Filtering.Active() {
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: gui.Tr.SLocalize("exitFilterMode"),
|
||||
displayString: gui.Tr.LcExitFilterMode,
|
||||
onPress: func() error {
|
||||
gui.State.Modes.Filtering.Path = ""
|
||||
return gui.Errors.ErrRestart
|
||||
@ -58,5 +58,5 @@ func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) erro
|
||||
})
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SLocalize("FilteringMenuTitle"), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.createMenu(gui.Tr.FilteringMenuTitle, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) error {
|
||||
@ -28,7 +29,7 @@ func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) err
|
||||
}
|
||||
|
||||
if branchType == "" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("NotAGitFlowBranch"))
|
||||
return gui.createErrorPanel(gui.Tr.NotAGitFlowBranch)
|
||||
}
|
||||
|
||||
subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "finish", suffix)
|
||||
@ -50,7 +51,7 @@ func (gui *Gui) handleCreateGitFlowMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
startHandler := func(branchType string) func() error {
|
||||
return func() error {
|
||||
title := gui.Tr.TemplateLocalize("NewBranchNamePrompt", map[string]interface{}{"branchType": branchType})
|
||||
title := utils.ResolvePlaceholderString(gui.Tr.NewGitFlowBranchPrompt, map[string]string{"branchType": branchType})
|
||||
return gui.prompt(title, "", func(name string) error {
|
||||
subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "start", name)
|
||||
gui.SubProcess = subProcess
|
||||
|
@ -149,7 +149,7 @@ func (gui *Gui) handleInfoClick(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
for _, mode := range gui.modeStatuses() {
|
||||
if mode.isActive() {
|
||||
if width-cx > len(gui.Tr.SLocalize("(reset)")) {
|
||||
if width-cx > len(gui.Tr.ResetInParentheses) {
|
||||
return nil
|
||||
}
|
||||
return mode.reset()
|
||||
@ -157,7 +157,7 @@ func (gui *Gui) handleInfoClick(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
// if we're not in an active mode we show the donate button
|
||||
if cx <= len(gui.Tr.SLocalize("Donate"))+len(INFO_SECTION_PADDING) {
|
||||
if cx <= len(gui.Tr.Donate)+len(INFO_SECTION_PADDING) {
|
||||
return gui.OSCommand.OpenLink("https://github.com/sponsors/jesseduffield")
|
||||
}
|
||||
return nil
|
||||
@ -175,7 +175,7 @@ func (gui *Gui) fetch(canPromptForCredentials bool) (err error) {
|
||||
err = gui.GitCommand.Fetch(fetchOpts)
|
||||
|
||||
if canPromptForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") {
|
||||
gui.createErrorPanel(gui.Tr.SLocalize("PassUnameWrong"))
|
||||
gui.createErrorPanel(gui.Tr.PassUnameWrong)
|
||||
}
|
||||
|
||||
gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC})
|
||||
|
@ -67,8 +67,8 @@ type SentinelErrors struct {
|
||||
// localising things in the code.
|
||||
func (gui *Gui) GenerateSentinelErrors() {
|
||||
gui.Errors = SentinelErrors{
|
||||
ErrSubProcess: errors.New(gui.Tr.SLocalize("RunningSubprocess")),
|
||||
ErrNoFiles: errors.New(gui.Tr.SLocalize("NoChangedFiles")),
|
||||
ErrSubProcess: errors.New(gui.Tr.RunningSubprocess),
|
||||
ErrNoFiles: errors.New(gui.Tr.NoChangedFiles),
|
||||
ErrSwitchRepo: errors.New("switching repo"),
|
||||
ErrRestart: errors.New("restarting"),
|
||||
}
|
||||
@ -83,9 +83,6 @@ func (gui *Gui) sentinelErrorsArr() []error {
|
||||
}
|
||||
}
|
||||
|
||||
// Teml is short for template used to make the required map[string]interface{} shorter when using gui.Tr.SLocalize and gui.Tr.TemplateLocalize
|
||||
type Teml i18n.Teml
|
||||
|
||||
// Gui wraps the gocui Gui object which handles rendering and events
|
||||
type Gui struct {
|
||||
g *gocui.Gui
|
||||
@ -95,7 +92,7 @@ type Gui struct {
|
||||
SubProcess *exec.Cmd
|
||||
State *guiState
|
||||
Config config.AppConfigurer
|
||||
Tr *i18n.Localizer
|
||||
Tr *i18n.TranslationSet
|
||||
Errors SentinelErrors
|
||||
Updater *updates.Updater
|
||||
statusManager *statusManager
|
||||
@ -391,7 +388,7 @@ func (gui *Gui) resetState() {
|
||||
|
||||
// for now the split view will always be on
|
||||
// NewGui builds a new gui handler
|
||||
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *oscommands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater, filterPath string, showRecentRepos bool) (*Gui, error) {
|
||||
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *oscommands.OSCommand, tr *i18n.TranslationSet, config config.AppConfigurer, updater *updates.Updater, filterPath string, showRecentRepos bool) (*Gui, error) {
|
||||
gui := &Gui{
|
||||
Log: log,
|
||||
GitCommand: gitCommand,
|
||||
@ -533,7 +530,7 @@ func (gui *Gui) runCommand() error {
|
||||
gui.SubProcess.Stdin = nil
|
||||
gui.SubProcess = nil
|
||||
|
||||
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.SLocalize("pressEnterToReturn"), color.FgGreen))
|
||||
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen))
|
||||
fmt.Scanln() // wait for enter press
|
||||
|
||||
return nil
|
||||
@ -580,7 +577,7 @@ func (gui *Gui) showIntroPopupMessage(done chan struct{}) error {
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: "",
|
||||
prompt: gui.Tr.SLocalize("IntroPopupMessage"),
|
||||
prompt: gui.Tr.IntroPopupMessage,
|
||||
handleConfirm: onConfirm,
|
||||
handleClose: onConfirm,
|
||||
})
|
||||
@ -610,8 +607,8 @@ func (gui *Gui) startBackgroundFetch() {
|
||||
err := gui.fetch(false)
|
||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||
_ = gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("NoAutomaticGitFetchTitle"),
|
||||
prompt: gui.Tr.SLocalize("NoAutomaticGitFetchBody"),
|
||||
title: gui.Tr.NoAutomaticGitFetchTitle,
|
||||
prompt: gui.Tr.NoAutomaticGitFetchBody,
|
||||
})
|
||||
} else {
|
||||
gui.goEvery(time.Second*60, gui.stopChan, func() error {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ func (gui *Gui) informationStr() string {
|
||||
}
|
||||
|
||||
if gui.g.Mouse {
|
||||
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.SLocalize("Donate"))
|
||||
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.Donate)
|
||||
return donate + " " + gui.Config.GetVersion()
|
||||
} else {
|
||||
return gui.Config.GetVersion()
|
||||
@ -37,7 +37,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
|
||||
v.Title = gui.Tr.NotEnoughSpace
|
||||
v.Wrap = true
|
||||
_, _ = g.SetViewOnTop("limit")
|
||||
}
|
||||
@ -104,7 +104,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
v.Title = gui.Tr.SLocalize("DiffTitle")
|
||||
v.Title = gui.Tr.DiffTitle
|
||||
v.Wrap = true
|
||||
v.FgColor = textColor
|
||||
v.IgnoreCarriageReturns = true
|
||||
@ -115,7 +115,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
secondaryView.Title = gui.Tr.SLocalize("DiffTitle")
|
||||
secondaryView.Title = gui.Tr.DiffTitle
|
||||
secondaryView.Wrap = true
|
||||
secondaryView.FgColor = textColor
|
||||
secondaryView.IgnoreCarriageReturns = true
|
||||
@ -127,7 +127,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
v.Title = gui.Tr.SLocalize("StatusTitle")
|
||||
v.Title = gui.Tr.StatusTitle
|
||||
v.FgColor = textColor
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
filesView.Highlight = true
|
||||
filesView.Title = gui.Tr.SLocalize("FilesTitle")
|
||||
filesView.Title = gui.Tr.FilesTitle
|
||||
filesView.ContainsList = true
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
branchesView.Title = gui.Tr.SLocalize("BranchesTitle")
|
||||
branchesView.Title = gui.Tr.BranchesTitle
|
||||
branchesView.FgColor = textColor
|
||||
branchesView.ContainsList = true
|
||||
}
|
||||
@ -156,7 +156,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
commitFilesView.Title = gui.Tr.SLocalize("CommitFiles")
|
||||
commitFilesView.Title = gui.Tr.CommitFiles
|
||||
commitFilesView.FgColor = textColor
|
||||
commitFilesView.ContainsList = true
|
||||
}
|
||||
@ -166,7 +166,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
commitsView.Title = gui.Tr.SLocalize("CommitsTitle")
|
||||
commitsView.Title = gui.Tr.CommitsTitle
|
||||
commitsView.FgColor = textColor
|
||||
commitsView.ContainsList = true
|
||||
}
|
||||
@ -176,7 +176,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
stashView.Title = gui.Tr.SLocalize("StashTitle")
|
||||
stashView.Title = gui.Tr.StashTitle
|
||||
stashView.FgColor = textColor
|
||||
stashView.ContainsList = true
|
||||
}
|
||||
@ -188,7 +188,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
_, _ = g.SetViewOnBottom("commitMessage")
|
||||
commitMessageView.Title = gui.Tr.SLocalize("CommitMessage")
|
||||
commitMessageView.Title = gui.Tr.CommitMessage
|
||||
commitMessageView.FgColor = textColor
|
||||
commitMessageView.Editable = true
|
||||
commitMessageView.Editor = gocui.EditorFunc(gui.commitMessageEditor)
|
||||
@ -202,7 +202,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
_, _ = g.SetViewOnBottom("credentials")
|
||||
credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername")
|
||||
credentialsView.Title = gui.Tr.CredentialsUsername
|
||||
credentialsView.FgColor = textColor
|
||||
credentialsView.Editable = true
|
||||
}
|
||||
|
@ -517,9 +517,9 @@ func (gui *Gui) getListContextKeyBindings() []*Binding {
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.NextItem), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.PrevPage), Modifier: gocui.ModNone, Handler: listContext.handlePrevPage, Description: gui.Tr.SLocalize("prevPage")},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.NextPage), Modifier: gocui.ModNone, Handler: listContext.handleNextPage, Description: gui.Tr.SLocalize("nextPage")},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.GotoTop), Modifier: gocui.ModNone, Handler: listContext.handleGotoTop, Description: gui.Tr.SLocalize("gotoTop")},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.PrevPage), Modifier: gocui.ModNone, Handler: listContext.handlePrevPage, Description: gui.Tr.LcPrevPage},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.NextPage), Modifier: gocui.ModNone, Handler: listContext.handleNextPage, Description: gui.Tr.LcNextPage},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gui.getKey(keybindingConfig.Universal.GotoTop), Modifier: gocui.ModNone, Handler: listContext.handleGotoTop, Description: gui.Tr.LcGotoTop},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{listContext.ContextKey}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listContext.handleClick},
|
||||
}...)
|
||||
@ -538,7 +538,7 @@ func (gui *Gui) getListContextKeyBindings() []*Binding {
|
||||
Contexts: []string{listContext.ContextKey},
|
||||
Key: gui.getKey(keybindingConfig.Universal.StartSearch),
|
||||
Handler: openSearchHandler,
|
||||
Description: gui.Tr.SLocalize("startSearch"),
|
||||
Description: gui.Tr.LcStartSearch,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
@ -546,7 +546,7 @@ func (gui *Gui) getListContextKeyBindings() []*Binding {
|
||||
Contexts: []string{listContext.ContextKey},
|
||||
Key: gui.getKey(keybindingConfig.Universal.GotoBottom),
|
||||
Handler: gotoBottomHandler,
|
||||
Description: gui.Tr.SLocalize("gotoBottom"),
|
||||
Description: gui.Tr.LcGotoBottom,
|
||||
Tag: "navigation",
|
||||
},
|
||||
}...)
|
||||
|
@ -36,9 +36,9 @@ func (gui *Gui) getMenuOptions() map[string]string {
|
||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||
|
||||
return map[string]string{
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Return): gui.Tr.SLocalize("close"),
|
||||
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.SLocalize("navigate"),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Select): gui.Tr.SLocalize("execute"),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Return): gui.Tr.LcClose,
|
||||
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.LcNavigate,
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Select): gui.Tr.LcExecute,
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
|
||||
if createMenuOptions.showCancel {
|
||||
// this is mutative but I'm okay with that for now
|
||||
items = append(items, &menuItem{
|
||||
displayStrings: []string{gui.Tr.SLocalize("cancel")},
|
||||
displayStrings: []string{gui.Tr.LcCancel},
|
||||
onPress: func() error {
|
||||
return nil
|
||||
},
|
||||
|
@ -244,7 +244,7 @@ func (gui *Gui) refreshMergePanel() error {
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
main: &viewUpdateOpts{
|
||||
title: gui.Tr.SLocalize("MergeConflictsTitle"),
|
||||
title: gui.Tr.MergeConflictsTitle,
|
||||
task: gui.createRenderStringWithoutScrollTask(content),
|
||||
noWrap: true,
|
||||
},
|
||||
@ -254,11 +254,11 @@ func (gui *Gui) refreshMergePanel() error {
|
||||
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
||||
item := gui.getSelectedFile()
|
||||
if item == nil {
|
||||
return "", errors.New(gui.Tr.SLocalize("NoFilesDisplay"))
|
||||
return "", errors.New(gui.Tr.NoFilesDisplay)
|
||||
}
|
||||
|
||||
if item.Type != "file" {
|
||||
return "", errors.New(gui.Tr.SLocalize("NotAFile"))
|
||||
return "", errors.New(gui.Tr.NotAFile)
|
||||
}
|
||||
|
||||
cat, err := gui.GitCommand.CatFile(item.Name)
|
||||
@ -294,11 +294,11 @@ func (gui *Gui) getMergingOptions() map[string]string {
|
||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||
|
||||
return map[string]string{
|
||||
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.SLocalize("selectHunk"),
|
||||
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevBlock), gui.getKeyDisplay(keybindingConfig.Universal.NextBlock)): gui.Tr.SLocalize("navigateConflicts"),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Select): gui.Tr.SLocalize("pickHunk"),
|
||||
gui.getKeyDisplay(keybindingConfig.Main.PickBothHunks): gui.Tr.SLocalize("pickBothHunks"),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Undo): gui.Tr.SLocalize("undo"),
|
||||
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.LcSelectHunk,
|
||||
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevBlock), gui.getKeyDisplay(keybindingConfig.Universal.NextBlock)): gui.Tr.LcNavigateConflicts,
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Select): gui.Tr.LcPickHunk,
|
||||
gui.getKeyDisplay(keybindingConfig.Main.PickBothHunks): gui.Tr.LcPickBothHunks,
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Undo): gui.Tr.LcUndo,
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +342,7 @@ func (gui *Gui) promptToContinue() error {
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: "continue",
|
||||
prompt: gui.Tr.SLocalize("ConflictsResolved"),
|
||||
prompt: gui.Tr.ConflictsResolved,
|
||||
handlersManageFocus: true,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.switchContext(gui.Contexts.Files.Context); err != nil {
|
||||
|
@ -19,7 +19,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
isActive: gui.State.Modes.Diffing.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%s %s %s", gui.Tr.SLocalize("showingGitDiff"), "git diff "+gui.diffStr(), utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)),
|
||||
fmt.Sprintf("%s %s %s", gui.Tr.LcShowingGitDiff, "git diff "+gui.diffStr(), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgMagenta,
|
||||
)
|
||||
},
|
||||
@ -29,7 +29,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
isActive: gui.State.Modes.Filtering.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%s '%s' %s", gui.Tr.SLocalize("filteringBy"), gui.State.Modes.Filtering.Path, utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)),
|
||||
fmt.Sprintf("%s '%s' %s", gui.Tr.LcFilteringBy, gui.State.Modes.Filtering.Path, utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgRed,
|
||||
color.Bold,
|
||||
)
|
||||
@ -40,7 +40,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
isActive: gui.GitCommand.PatchManager.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%s %s", gui.Tr.SLocalize("buildingPatch"), utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)),
|
||||
fmt.Sprintf("%s %s", gui.Tr.LcBuildingPatch, utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgYellow,
|
||||
color.Bold,
|
||||
)
|
||||
@ -51,7 +51,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
isActive: gui.State.Modes.CherryPicking.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%d commits copied %s", len(gui.State.Modes.CherryPicking.CherryPickedCommits), utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)),
|
||||
fmt.Sprintf("%d commits copied %s", len(gui.State.Modes.CherryPicking.CherryPickedCommits), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgCyan,
|
||||
)
|
||||
},
|
||||
|
@ -54,5 +54,5 @@ func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(strings.Title(gui.Tr.SLocalize("menu")), menuItems, createMenuOptions{})
|
||||
return gui.createMenu(strings.Title(gui.Tr.LcMenu), menuItems, createMenuOptions{})
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
if !gui.GitCommand.PatchManager.Active() {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("NoPatchError"))
|
||||
return gui.createErrorPanel(gui.Tr.NoPatchError)
|
||||
}
|
||||
|
||||
menuItems := []*menuItem{
|
||||
@ -61,7 +61,7 @@ func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SLocalize("PatchOptionsTitle"), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.createMenu(gui.Tr.PatchOptionsTitle, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) getPatchCommitIndex() int {
|
||||
@ -75,7 +75,7 @@ func (gui *Gui) getPatchCommitIndex() int {
|
||||
|
||||
func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
|
||||
if gui.GitCommand.WorkingTreeState() != "normal" {
|
||||
return false, gui.createErrorPanel(gui.Tr.SLocalize("CantPatchWhileRebasingError"))
|
||||
return false, gui.createErrorPanel(gui.Tr.CantPatchWhileRebasingError)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@ -96,7 +96,7 @@ func (gui *Gui) handleDeletePatchFromCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
err := gui.GitCommand.DeletePatchesFromCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -112,7 +112,7 @@ func (gui *Gui) handleMovePatchToSelectedCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
err := gui.GitCommand.MovePatchToSelectedCommit(gui.State.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx, gui.GitCommand.PatchManager)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -129,7 +129,7 @@ func (gui *Gui) handlePullPatchIntoWorkingTree() error {
|
||||
}
|
||||
|
||||
pull := func(stash bool) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
err := gui.GitCommand.PullPatchIntoIndex(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager, stash)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -138,8 +138,8 @@ func (gui *Gui) handlePullPatchIntoWorkingTree() error {
|
||||
|
||||
if len(gui.trackedFiles()) > 0 {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("MustStashTitle"),
|
||||
prompt: gui.Tr.SLocalize("MustStashWarning"),
|
||||
title: gui.Tr.MustStashTitle,
|
||||
prompt: gui.Tr.MustStashWarning,
|
||||
handleConfirm: func() error {
|
||||
return pull(true)
|
||||
},
|
||||
@ -158,7 +158,7 @@ func (gui *Gui) handlePullPatchIntoNewCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
err := gui.GitCommand.PullPatchIntoNewCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
|
@ -75,7 +75,7 @@ func (gui *Gui) quit() error {
|
||||
if gui.Config.GetUserConfig().ConfirmOnQuit {
|
||||
return gui.ask(askOpts{
|
||||
title: "",
|
||||
prompt: gui.Tr.SLocalize("ConfirmQuit"),
|
||||
prompt: gui.Tr.ConfirmQuit,
|
||||
handleConfirm: func() error {
|
||||
return gocui.ErrQuit
|
||||
},
|
||||
|
@ -26,9 +26,9 @@ func (gui *Gui) handleCreateRebaseOptionsMenu() error {
|
||||
|
||||
var title string
|
||||
if gui.GitCommand.WorkingTreeState() == "merging" {
|
||||
title = gui.Tr.SLocalize("MergeOptionsTitle")
|
||||
title = gui.Tr.MergeOptionsTitle
|
||||
} else {
|
||||
title = gui.Tr.SLocalize("RebaseOptionsTitle")
|
||||
title = gui.Tr.RebaseOptionsTitle
|
||||
}
|
||||
|
||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||
@ -38,7 +38,7 @@ func (gui *Gui) genericMergeCommand(command string) error {
|
||||
status := gui.GitCommand.WorkingTreeState()
|
||||
|
||||
if status != "merging" && status != "rebasing" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("NotMergingOrRebasing"))
|
||||
return gui.createErrorPanel(gui.Tr.NotMergingOrRebasing)
|
||||
}
|
||||
|
||||
commandType := strings.Replace(status, "ing", "e", 1)
|
||||
@ -77,8 +77,8 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
||||
return nil
|
||||
} else if strings.Contains(result.Error(), "When you have resolved this problem") || strings.Contains(result.Error(), "fix conflicts") || strings.Contains(result.Error(), "Resolve all conflicts manually") {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("FoundConflictsTitle"),
|
||||
prompt: gui.Tr.SLocalize("FoundConflicts"),
|
||||
title: gui.Tr.FoundConflictsTitle,
|
||||
prompt: gui.Tr.FoundConflicts,
|
||||
handlersManageFocus: true,
|
||||
handleConfirm: func() error {
|
||||
return gui.switchContext(gui.Contexts.Files.Context)
|
||||
|
@ -29,7 +29,7 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SLocalize("RecentRepos"), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.createMenu(gui.Tr.RecentRepos, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) dispatchSwitchToRepo(path string) error {
|
||||
|
@ -90,8 +90,8 @@ func (gui *Gui) handleCheckoutReflogCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
err := gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("checkoutCommit"),
|
||||
prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"),
|
||||
title: gui.Tr.LcCheckoutCommit,
|
||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||
},
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -52,13 +53,13 @@ func (gui *Gui) handleDeleteRemoteBranch(g *gocui.Gui, v *gocui.View) error {
|
||||
if remoteBranch == nil {
|
||||
return nil
|
||||
}
|
||||
message := fmt.Sprintf("%s '%s'?", gui.Tr.SLocalize("DeleteRemoteBranchMessage"), remoteBranch.FullName())
|
||||
message := fmt.Sprintf("%s '%s'?", gui.Tr.DeleteRemoteBranchMessage, remoteBranch.FullName())
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("DeleteRemoteBranch"),
|
||||
title: gui.Tr.DeleteRemoteBranch,
|
||||
prompt: message,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("DeletingStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||
if err := gui.GitCommand.DeleteRemoteBranch(remoteBranch.RemoteName, remoteBranch.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -78,16 +79,16 @@ func (gui *Gui) handleSetBranchUpstream(g *gocui.Gui, v *gocui.View) error {
|
||||
selectedBranch := gui.getSelectedRemoteBranch()
|
||||
checkedOutBranch := gui.getCheckedOutBranch()
|
||||
|
||||
message := gui.Tr.TemplateLocalize(
|
||||
"SetUpstreamMessage",
|
||||
Teml{
|
||||
message := utils.ResolvePlaceholderString(
|
||||
gui.Tr.SetUpstreamMessage,
|
||||
map[string]string{
|
||||
"checkedOut": checkedOutBranch.Name,
|
||||
"selected": selectedBranch.FullName(),
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("SetUpstreamTitle"),
|
||||
title: gui.Tr.SetUpstreamTitle,
|
||||
prompt: message,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.SetBranchUpstream(selectedBranch.RemoteName, selectedBranch.Name, checkedOutBranch.Name); err != nil {
|
||||
|
@ -80,8 +80,8 @@ func (gui *Gui) handleRemoteEnter() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleAddRemote(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.prompt(gui.Tr.SLocalize("newRemoteName"), "", func(remoteName string) error {
|
||||
return gui.prompt(gui.Tr.SLocalize("newRemoteUrl"), "", func(remoteUrl string) error {
|
||||
return gui.prompt(gui.Tr.LcNewRemoteName, "", func(remoteName string) error {
|
||||
return gui.prompt(gui.Tr.LcNewRemoteUrl, "", func(remoteUrl string) error {
|
||||
if err := gui.GitCommand.AddRemote(remoteName, remoteUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -97,8 +97,8 @@ func (gui *Gui) handleRemoveRemote(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("removeRemote"),
|
||||
prompt: gui.Tr.SLocalize("removeRemotePrompt") + " '" + remote.Name + "'?",
|
||||
title: gui.Tr.LcRemoveRemote,
|
||||
prompt: gui.Tr.LcRemoveRemotePrompt + " '" + remote.Name + "'?",
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.RemoveRemote(remote.Name); err != nil {
|
||||
return err
|
||||
@ -115,9 +115,9 @@ func (gui *Gui) handleEditRemote(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
editNameMessage := gui.Tr.TemplateLocalize(
|
||||
"editRemoteName",
|
||||
Teml{
|
||||
editNameMessage := utils.ResolvePlaceholderString(
|
||||
gui.Tr.LcEditRemoteName,
|
||||
map[string]string{
|
||||
"remoteName": remote.Name,
|
||||
},
|
||||
)
|
||||
@ -129,9 +129,9 @@ func (gui *Gui) handleEditRemote(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
}
|
||||
|
||||
editUrlMessage := gui.Tr.TemplateLocalize(
|
||||
"editRemoteUrl",
|
||||
Teml{
|
||||
editUrlMessage := utils.ResolvePlaceholderString(
|
||||
gui.Tr.LcEditRemoteUrl,
|
||||
map[string]string{
|
||||
"remoteName": updatedRemoteName,
|
||||
},
|
||||
)
|
||||
@ -157,7 +157,7 @@ func (gui *Gui) handleFetchRemote(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("FetchingRemoteStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
|
||||
gui.State.FetchMutex.Lock()
|
||||
defer gui.State.FetchMutex.Unlock()
|
||||
|
||||
|
@ -46,5 +46,5 @@ func (gui *Gui) createResetMenu(ref string) error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(fmt.Sprintf("%s %s", gui.Tr.SLocalize("resetTo"), ref), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.createMenu(fmt.Sprintf("%s %s", gui.Tr.LcResetTo, ref), menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
@ -35,11 +35,11 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
|
||||
}
|
||||
|
||||
if secondaryFocused {
|
||||
gui.getMainView().Title = gui.Tr.SLocalize("StagedChanges")
|
||||
gui.getSecondaryView().Title = gui.Tr.SLocalize("UnstagedChanges")
|
||||
gui.getMainView().Title = gui.Tr.StagedChanges
|
||||
gui.getSecondaryView().Title = gui.Tr.UnstagedChanges
|
||||
} else {
|
||||
gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges")
|
||||
gui.getSecondaryView().Title = gui.Tr.SLocalize("StagedChanges")
|
||||
gui.getMainView().Title = gui.Tr.UnstagedChanges
|
||||
gui.getSecondaryView().Title = gui.Tr.StagedChanges
|
||||
}
|
||||
|
||||
// note for custom diffs, we'll need to send a flag here saying not to use the custom diff
|
||||
@ -105,8 +105,8 @@ func (gui *Gui) handleResetSelection(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
if !gui.Config.GetUserConfig().Gui.SkipUnstageLineWarning {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("UnstageLinesTitle"),
|
||||
prompt: gui.Tr.SLocalize("UnstageLinesPrompt"),
|
||||
title: gui.Tr.UnstageLinesTitle,
|
||||
prompt: gui.Tr.UnstageLinesPrompt,
|
||||
handlersManageFocus: true,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.switchContext(gui.Contexts.Staging.Context); err != nil {
|
||||
|
@ -3,6 +3,7 @@ package gui
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -20,7 +21,7 @@ func (gui *Gui) handleStashEntrySelect() error {
|
||||
var task updateTask
|
||||
stashEntry := gui.getSelectedStashEntry()
|
||||
if stashEntry == nil {
|
||||
task = gui.createRenderStringTask(gui.Tr.SLocalize("NoStashEntries"))
|
||||
task = gui.createRenderStringTask(gui.Tr.NoStashEntries)
|
||||
} else {
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
|
||||
@ -56,8 +57,8 @@ func (gui *Gui) handleStashApply(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("StashApply"),
|
||||
prompt: gui.Tr.SLocalize("SureApplyStashEntry"),
|
||||
title: gui.Tr.StashApply,
|
||||
prompt: gui.Tr.SureApplyStashEntry,
|
||||
handleConfirm: func() error {
|
||||
return apply()
|
||||
},
|
||||
@ -76,8 +77,8 @@ func (gui *Gui) handleStashPop(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("StashPop"),
|
||||
prompt: gui.Tr.SLocalize("SurePopStashEntry"),
|
||||
title: gui.Tr.StashPop,
|
||||
prompt: gui.Tr.SurePopStashEntry,
|
||||
handleConfirm: func() error {
|
||||
return pop()
|
||||
},
|
||||
@ -86,8 +87,8 @@ func (gui *Gui) handleStashPop(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
func (gui *Gui) handleStashDrop(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("StashDrop"),
|
||||
prompt: gui.Tr.SLocalize("SureDropStashEntry"),
|
||||
title: gui.Tr.StashDrop,
|
||||
prompt: gui.Tr.SureDropStashEntry,
|
||||
handleConfirm: func() error {
|
||||
return gui.stashDo("drop")
|
||||
},
|
||||
@ -97,12 +98,13 @@ func (gui *Gui) handleStashDrop(g *gocui.Gui, v *gocui.View) error {
|
||||
func (gui *Gui) stashDo(method string) error {
|
||||
stashEntry := gui.getSelectedStashEntry()
|
||||
if stashEntry == nil {
|
||||
errorMessage := gui.Tr.TemplateLocalize(
|
||||
"NoStashTo",
|
||||
Teml{
|
||||
errorMessage := utils.ResolvePlaceholderString(
|
||||
gui.Tr.NoStashTo,
|
||||
map[string]string{
|
||||
"method": method,
|
||||
},
|
||||
)
|
||||
|
||||
return gui.createErrorPanel(errorMessage)
|
||||
}
|
||||
if err := gui.GitCommand.StashDo(stashEntry.Index, method); err != nil {
|
||||
@ -113,9 +115,9 @@ func (gui *Gui) stashDo(method string) error {
|
||||
|
||||
func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
||||
if len(gui.trackedFiles()) == 0 && len(gui.stagedFiles()) == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("NoTrackedStagedFilesStash"))
|
||||
return gui.createErrorPanel(gui.Tr.NoTrackedStagedFilesStash)
|
||||
}
|
||||
return gui.prompt(gui.Tr.SLocalize("StashChanges"), "", func(stashComment string) error {
|
||||
return gui.prompt(gui.Tr.StashChanges, "", func(stashComment string) error {
|
||||
if err := stashFunc(stashComment); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ func cursorInSubstring(cx int, prefix string, substring string) bool {
|
||||
|
||||
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
||||
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
||||
return gui.createLoaderPanel(v, gui.Tr.SLocalize("CheckingForUpdates"))
|
||||
return gui.createLoaderPanel(v, gui.Tr.CheckingForUpdates)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStatusClick(g *gocui.Gui, v *gocui.View) error {
|
||||
|
@ -46,8 +46,8 @@ func (gui *Gui) handleCheckoutSubCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
err := gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("checkoutCommit"),
|
||||
prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"),
|
||||
title: gui.Tr.LcCheckoutCommit,
|
||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||
},
|
||||
|
@ -79,8 +79,8 @@ func (gui *Gui) enterSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
|
||||
func (gui *Gui) removeSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("RemoveSubmodule"),
|
||||
prompt: gui.Tr.SLocalizef("RemoveSubmodulePrompt", submodule.Name),
|
||||
title: gui.Tr.RemoveSubmodule,
|
||||
prompt: fmt.Sprintf(gui.Tr.RemoveSubmodulePrompt, submodule.Name),
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.SubmoduleDelete(submodule); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
@ -92,7 +92,7 @@ func (gui *Gui) removeSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleResetSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("resettingSubmoduleStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcResettingSubmoduleStatus, func() error {
|
||||
return gui.resetSubmodule(submodule)
|
||||
})
|
||||
}
|
||||
@ -126,12 +126,12 @@ func (gui *Gui) resetSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleAddSubmodule() error {
|
||||
return gui.prompt(gui.Tr.SLocalize("newSubmoduleUrl"), "", func(submoduleUrl string) error {
|
||||
return gui.prompt(gui.Tr.LcNewSubmoduleUrl, "", func(submoduleUrl string) error {
|
||||
nameSuggestion := filepath.Base(strings.TrimSuffix(submoduleUrl, filepath.Ext(submoduleUrl)))
|
||||
|
||||
return gui.prompt(gui.Tr.SLocalize("newSubmoduleName"), nameSuggestion, func(submoduleName string) error {
|
||||
return gui.prompt(gui.Tr.SLocalize("newSubmodulePath"), submoduleName, func(submodulePath string) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("addingSubmoduleStatus"), func() error {
|
||||
return gui.prompt(gui.Tr.LcNewSubmoduleName, nameSuggestion, func(submoduleName string) error {
|
||||
return gui.prompt(gui.Tr.LcNewSubmodulePath, submoduleName, func(submodulePath string) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcAddingSubmoduleStatus, func() error {
|
||||
err := gui.GitCommand.SubmoduleAdd(submoduleName, submodulePath, submoduleUrl)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
@ -143,8 +143,8 @@ func (gui *Gui) handleAddSubmodule() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEditSubmoduleUrl(submodule *models.SubmoduleConfig) error {
|
||||
return gui.prompt(gui.Tr.SLocalizef("updateSubmoduleUrl", submodule.Name), submodule.Url, func(newUrl string) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("updatingSubmoduleUrlStatus"), func() error {
|
||||
return gui.prompt(fmt.Sprintf(gui.Tr.LcUpdateSubmoduleUrl, submodule.Name), submodule.Url, func(newUrl string) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcUpdatingSubmoduleUrlStatus, func() error {
|
||||
err := gui.GitCommand.SubmoduleUpdateUrl(submodule.Name, submodule.Path, newUrl)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
@ -154,7 +154,7 @@ func (gui *Gui) handleEditSubmoduleUrl(submodule *models.SubmoduleConfig) error
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSubmoduleInit(submodule *models.SubmoduleConfig) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("initializingSubmoduleStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcInitializingSubmoduleStatus, func() error {
|
||||
err := gui.GitCommand.SubmoduleInit(submodule.Path)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
@ -178,13 +178,13 @@ func (gui *Gui) forSubmodule(callback func(*models.SubmoduleConfig) error) func(
|
||||
func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("submoduleStashAndReset"),
|
||||
displayString: gui.Tr.LcSubmoduleStashAndReset,
|
||||
onPress: func() error {
|
||||
return gui.resetSubmodule(submodule)
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.SLocalize("removeSubmodule"),
|
||||
displayString: gui.Tr.LcRemoveSubmodule,
|
||||
onPress: func() error {
|
||||
return gui.removeSubmodule(submodule)
|
||||
},
|
||||
@ -197,9 +197,9 @@ func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) er
|
||||
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayStrings: []string{gui.Tr.SLocalize("bulkInitSubmodules"), utils.ColoredString(gui.GitCommand.SubmoduleBulkInitCmdStr(), color.FgGreen)},
|
||||
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkInitCmdStr(), color.FgGreen)},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("runningCommand"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.OSCommand.RunCommand(gui.GitCommand.SubmoduleBulkInitCmdStr()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@ -209,9 +209,9 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.SLocalize("bulkUpdateSubmodules"), utils.ColoredString(gui.GitCommand.SubmoduleBulkUpdateCmdStr(), color.FgYellow)},
|
||||
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkUpdateCmdStr(), color.FgYellow)},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("runningCommand"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.OSCommand.RunCommand(gui.GitCommand.SubmoduleBulkUpdateCmdStr()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@ -221,9 +221,9 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.SLocalize("submoduleStashAndReset"), utils.ColoredString(fmt.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr()), color.FgRed)},
|
||||
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, utils.ColoredString(fmt.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr()), color.FgRed)},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("runningCommand"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.GitCommand.ResetSubmodules(gui.State.Submodules); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@ -233,9 +233,9 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.SLocalize("bulkDeinitSubmodules"), utils.ColoredString(gui.GitCommand.SubmoduleBulkDeinitCmdStr(), color.FgRed)},
|
||||
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkDeinitCmdStr(), color.FgRed)},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("runningCommand"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.OSCommand.RunCommand(gui.GitCommand.SubmoduleBulkDeinitCmdStr()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@ -246,11 +246,11 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SLocalize("bulkSubmoduleOptions"), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.createMenu(gui.Tr.LcBulkSubmoduleOptions, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleUpdateSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("updatingSubmoduleStatus"), func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcUpdatingSubmoduleStatus, func() error {
|
||||
err := gui.GitCommand.SubmoduleUpdate(submodule.Path)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
|
@ -3,6 +3,7 @@ package gui
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -64,15 +65,15 @@ func (gui *Gui) handleDeleteTag(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
prompt := gui.Tr.TemplateLocalize(
|
||||
"DeleteTagPrompt",
|
||||
Teml{
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
gui.Tr.DeleteTagPrompt,
|
||||
map[string]string{
|
||||
"tagName": tag.Name,
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("DeleteTagTitle"),
|
||||
title: gui.Tr.DeleteTagTitle,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
if err := gui.GitCommand.DeleteTag(tag.Name); err != nil {
|
||||
@ -89,9 +90,9 @@ func (gui *Gui) handlePushTag(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
title := gui.Tr.TemplateLocalize(
|
||||
"PushTagTitle",
|
||||
Teml{
|
||||
title := utils.ResolvePlaceholderString(
|
||||
gui.Tr.PushTagTitle,
|
||||
map[string]string{
|
||||
"tagName": tag.Name,
|
||||
},
|
||||
)
|
||||
@ -105,7 +106,7 @@ func (gui *Gui) handlePushTag(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateTag(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.prompt(gui.Tr.SLocalize("CreateTagTitle"), "", func(tagName string) error {
|
||||
return gui.prompt(gui.Tr.CreateTagTitle, "", func(tagName string) error {
|
||||
// leaving commit SHA blank so that we're just creating the tag for the current commit
|
||||
if err := gui.GitCommand.CreateLightweightTag(tagName, ""); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
|
@ -85,10 +85,10 @@ func (gui *Gui) parseReflogForActions(onUserAction func(counter int, action refl
|
||||
|
||||
func (gui *Gui) reflogUndo(g *gocui.Gui, v *gocui.View) error {
|
||||
undoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit undo]"}
|
||||
undoingStatus := gui.Tr.SLocalize("UndoingStatus")
|
||||
undoingStatus := gui.Tr.UndoingStatus
|
||||
|
||||
if gui.GitCommand.WorkingTreeState() == "rebasing" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("cantUndoWhileRebasing"))
|
||||
return gui.createErrorPanel(gui.Tr.LcCantUndoWhileRebasing)
|
||||
}
|
||||
|
||||
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
|
||||
@ -116,10 +116,10 @@ func (gui *Gui) reflogUndo(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
func (gui *Gui) reflogRedo(g *gocui.Gui, v *gocui.View) error {
|
||||
redoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit redo]"}
|
||||
redoingStatus := gui.Tr.SLocalize("RedoingStatus")
|
||||
redoingStatus := gui.Tr.RedoingStatus
|
||||
|
||||
if gui.GitCommand.WorkingTreeState() == "rebasing" {
|
||||
return gui.createErrorPanel(gui.Tr.SLocalize("cantRedoWhileRebasing"))
|
||||
return gui.createErrorPanel(gui.Tr.LcCantRedoWhileRebasing)
|
||||
}
|
||||
|
||||
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
|
||||
@ -167,11 +167,11 @@ func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHar
|
||||
if dirtyWorkingTree {
|
||||
// offer to autostash changes
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SLocalize("AutoStashTitle"),
|
||||
prompt: gui.Tr.SLocalize("AutoStashPrompt"),
|
||||
title: gui.Tr.AutoStashTitle,
|
||||
prompt: gui.Tr.AutoStashPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(options.WaitingStatus, func() error {
|
||||
if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + commitSha); err != nil {
|
||||
if err := gui.GitCommand.StashSave(gui.Tr.StashPrefix + commitSha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
if err := reset(); err != nil {
|
||||
|
@ -377,12 +377,12 @@ func (gui *Gui) globalOptionsMap() map[string]string {
|
||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||
|
||||
return map[string]string{
|
||||
fmt.Sprintf("%s/%s", gui.getKeyDisplay(keybindingConfig.Universal.ScrollUpMain), gui.getKeyDisplay(keybindingConfig.Universal.ScrollDownMain)): gui.Tr.SLocalize("scroll"),
|
||||
fmt.Sprintf("%s %s %s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevBlock), gui.getKeyDisplay(keybindingConfig.Universal.NextBlock), gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.SLocalize("navigate"),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Return): gui.Tr.SLocalize("cancel"),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Quit): gui.Tr.SLocalize("quit"),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.OptionMenu): gui.Tr.SLocalize("menu"),
|
||||
"1-5": gui.Tr.SLocalize("jump"),
|
||||
fmt.Sprintf("%s/%s", gui.getKeyDisplay(keybindingConfig.Universal.ScrollUpMain), gui.getKeyDisplay(keybindingConfig.Universal.ScrollDownMain)): gui.Tr.LcScroll,
|
||||
fmt.Sprintf("%s %s %s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevBlock), gui.getKeyDisplay(keybindingConfig.Universal.NextBlock), gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.LcNavigate,
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Return): gui.Tr.LcCancel,
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Quit): gui.Tr.LcQuit,
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.OptionMenu): gui.Tr.LcMenu,
|
||||
"1-5": gui.Tr.LcJump,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,13 +12,13 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
nukeStr := "reset --hard HEAD && git clean -fd"
|
||||
if len(gui.State.Submodules) > 0 {
|
||||
nukeStr = fmt.Sprintf("%s (%s)", nukeStr, gui.Tr.SLocalize("andResetSubmodules"))
|
||||
nukeStr = fmt.Sprintf("%s (%s)", nukeStr, gui.Tr.LcAndResetSubmodules)
|
||||
}
|
||||
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayStrings: []string{
|
||||
gui.Tr.SLocalize("discardAllChangesToAllFiles"),
|
||||
gui.Tr.LcDiscardAllChangesToAllFiles,
|
||||
red.Sprint(nukeStr),
|
||||
},
|
||||
onPress: func() error {
|
||||
@ -31,7 +31,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
gui.Tr.SLocalize("discardAnyUnstagedChanges"),
|
||||
gui.Tr.LcDiscardAnyUnstagedChanges,
|
||||
red.Sprint("git checkout -- ."),
|
||||
},
|
||||
onPress: func() error {
|
||||
@ -44,7 +44,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
gui.Tr.SLocalize("discardUntrackedFiles"),
|
||||
gui.Tr.LcDiscardUntrackedFiles,
|
||||
red.Sprint("git clean -fd"),
|
||||
},
|
||||
onPress: func() error {
|
||||
@ -57,7 +57,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
gui.Tr.SLocalize("softReset"),
|
||||
gui.Tr.LcSoftReset,
|
||||
red.Sprint("git reset --soft HEAD"),
|
||||
},
|
||||
onPress: func() error {
|
||||
@ -83,7 +83,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
gui.Tr.SLocalize("hardReset"),
|
||||
gui.Tr.LcHardReset,
|
||||
red.Sprint("git reset --hard HEAD"),
|
||||
},
|
||||
onPress: func() error {
|
||||
|
1543
pkg/i18n/dutch.go
1543
pkg/i18n/dutch.go
File diff suppressed because it is too large
Load Diff
2107
pkg/i18n/english.go
2107
pkg/i18n/english.go
File diff suppressed because it is too large
Load Diff
102
pkg/i18n/i18n.go
102
pkg/i18n/i18n.go
@ -1,89 +1,42 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudfoundry/jibber_jabber"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Teml is short for template used to make the required map[string]interface{} shorter when using gui.Tr.SLocalize and gui.Tr.TemplateLocalize
|
||||
type Teml map[string]interface{}
|
||||
|
||||
// Localizer will translate a message into the user's language
|
||||
type Localizer struct {
|
||||
i18nLocalizer *i18n.Localizer
|
||||
language string
|
||||
Log *logrus.Entry
|
||||
Log *logrus.Entry
|
||||
S TranslationSet
|
||||
}
|
||||
|
||||
// NewLocalizer creates a new Localizer
|
||||
func NewLocalizer(log *logrus.Entry) *Localizer {
|
||||
// NewTranslationSet creates a new Localizer
|
||||
func NewTranslationSet(log *logrus.Entry) *TranslationSet {
|
||||
userLang := detectLanguage(jibber_jabber.DetectLanguage)
|
||||
|
||||
log.Info("language: " + userLang)
|
||||
|
||||
return setupLocalizer(log, userLang)
|
||||
}
|
||||
baseSet := englishTranslationSet()
|
||||
|
||||
// Localize handels the translations
|
||||
// expects i18n.LocalizeConfig as input: https://godoc.org/github.com/nicksnyder/go-i18n/v2/i18n#Localizer.MustLocalize
|
||||
// output: translated string
|
||||
func (l *Localizer) Localize(config *i18n.LocalizeConfig) string {
|
||||
return l.i18nLocalizer.MustLocalize(config)
|
||||
}
|
||||
|
||||
// SLocalize (short localize) is for 1 line localizations
|
||||
// ID: The id that is used in the .toml translation files
|
||||
// Other: the default message it needs to return if there is no translation found or the system is english
|
||||
func (l *Localizer) SLocalize(ID string) string {
|
||||
return l.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: &i18n.Message{
|
||||
ID: ID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TemplateLocalize allows the Other input to be dynamic
|
||||
func (l *Localizer) TemplateLocalize(ID string, TemplateData map[string]interface{}) string {
|
||||
return l.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: &i18n.Message{
|
||||
ID: ID,
|
||||
},
|
||||
TemplateData: TemplateData,
|
||||
})
|
||||
}
|
||||
|
||||
func (l *Localizer) SLocalizef(ID string, args ...interface{}) string {
|
||||
str := l.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: &i18n.Message{
|
||||
ID: ID,
|
||||
},
|
||||
})
|
||||
|
||||
return fmt.Sprintf(str, args...)
|
||||
}
|
||||
|
||||
// GetLanguage returns the currently selected language, e.g 'en'
|
||||
func (l *Localizer) GetLanguage() string {
|
||||
return l.language
|
||||
}
|
||||
|
||||
// add translation file(s)
|
||||
func addBundles(log *logrus.Entry, i18nBundle *i18n.Bundle) {
|
||||
fs := []func(*i18n.Bundle) error{
|
||||
addPolish,
|
||||
addDutch,
|
||||
addEnglish,
|
||||
for languageCode, translationSet := range GetTranslationSets() {
|
||||
if strings.HasPrefix(userLang, languageCode) {
|
||||
_ = mergo.Merge(&baseSet, translationSet, mergo.WithOverride)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range fs {
|
||||
if err := f(i18nBundle); err != nil {
|
||||
log.Fatal(err)
|
||||
return &baseSet
|
||||
}
|
||||
|
||||
}
|
||||
// GetTranslationSets gets all the translation sets, keyed by language code
|
||||
func GetTranslationSets() map[string]TranslationSet {
|
||||
return map[string]TranslationSet{
|
||||
"pl": polishTranslationSet(),
|
||||
"nl": dutchTranslationSet(),
|
||||
"en": englishTranslationSet(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,20 +48,3 @@ func detectLanguage(langDetector func() (string, error)) string {
|
||||
|
||||
return "C"
|
||||
}
|
||||
|
||||
// setupLocalizer creates a new localizer using given userLang
|
||||
func setupLocalizer(log *logrus.Entry, userLang string) *Localizer {
|
||||
// create a i18n bundle that can be used to add translations and other things
|
||||
i18nBundle := i18n.NewBundle(language.English)
|
||||
|
||||
addBundles(log, i18nBundle)
|
||||
|
||||
// return the new localizer that can be used to translate text
|
||||
i18nLocalizer := i18n.NewLocalizer(i18nBundle, userLang)
|
||||
|
||||
return &Localizer{
|
||||
i18nLocalizer: i18nLocalizer,
|
||||
language: userLang,
|
||||
Log: log,
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -17,11 +15,6 @@ func getDummyLog() *logrus.Entry {
|
||||
return log.WithField("test", "test")
|
||||
}
|
||||
|
||||
// TestNewLocalizer is a function.
|
||||
func TestNewLocalizer(t *testing.T) {
|
||||
assert.NotNil(t, NewLocalizer(getDummyLog()))
|
||||
}
|
||||
|
||||
// TestDetectLanguage is a function.
|
||||
func TestDetectLanguage(t *testing.T) {
|
||||
type scenario struct {
|
||||
@ -48,44 +41,3 @@ func TestDetectLanguage(t *testing.T) {
|
||||
assert.EqualValues(t, s.expected, detectLanguage(s.langDetector))
|
||||
}
|
||||
}
|
||||
|
||||
// TestLocalizer is a function.
|
||||
func TestLocalizer(t *testing.T) {
|
||||
type scenario struct {
|
||||
userLang string
|
||||
test func(*Localizer)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"C",
|
||||
func(l *Localizer) {
|
||||
assert.EqualValues(t, "C", l.GetLanguage())
|
||||
assert.Equal(t, "Diff", l.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: &i18n.Message{
|
||||
ID: "DiffTitle",
|
||||
},
|
||||
}))
|
||||
assert.Equal(t, "Diff", l.SLocalize("DiffTitle"))
|
||||
assert.Equal(t, "Are you sure you want to delete the branch test?", l.TemplateLocalize("DeleteBranchMessage", Teml{"selectedBranchName": "test"}))
|
||||
},
|
||||
},
|
||||
{
|
||||
"nl",
|
||||
func(l *Localizer) {
|
||||
assert.EqualValues(t, "nl", l.GetLanguage())
|
||||
assert.Equal(t, "Diff", l.Localize(&i18n.LocalizeConfig{
|
||||
DefaultMessage: &i18n.Message{
|
||||
ID: "DiffTitle",
|
||||
},
|
||||
}))
|
||||
assert.Equal(t, "Diff", l.SLocalize("DiffTitle"))
|
||||
assert.Equal(t, "Weet je zeker dat je branch test wilt verwijderen?", l.TemplateLocalize("DeleteBranchMessage", Teml{"selectedBranchName": "test"}))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s.test(setupLocalizer(getDummyLog(), s.userLang))
|
||||
}
|
||||
}
|
||||
|
1013
pkg/i18n/polish.go
1013
pkg/i18n/polish.go
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -26,7 +27,7 @@ type Updater struct {
|
||||
Log *logrus.Entry
|
||||
Config config.AppConfigurer
|
||||
OSCommand *oscommands.OSCommand
|
||||
Tr *i18n.Localizer
|
||||
Tr *i18n.TranslationSet
|
||||
}
|
||||
|
||||
// Updaterer implements the check and update methods
|
||||
@ -40,7 +41,7 @@ const (
|
||||
)
|
||||
|
||||
// NewUpdater creates a new updater
|
||||
func NewUpdater(log *logrus.Entry, config config.AppConfigurer, osCommand *oscommands.OSCommand, tr *i18n.Localizer) (*Updater, error) {
|
||||
func NewUpdater(log *logrus.Entry, config config.AppConfigurer, osCommand *oscommands.OSCommand, tr *i18n.TranslationSet) (*Updater, error) {
|
||||
contextLogger := log.WithField("context", "updates")
|
||||
|
||||
return &Updater{
|
||||
@ -106,13 +107,12 @@ func (u *Updater) checkForNewUpdate() (string, error) {
|
||||
u.Log.Info("New version is " + newVersion)
|
||||
|
||||
if newVersion == currentVersion {
|
||||
return "", errors.New(u.Tr.SLocalize("OnLatestVersionErr"))
|
||||
return "", errors.New(u.Tr.OnLatestVersionErr)
|
||||
}
|
||||
|
||||
if u.majorVersionDiffers(currentVersion, newVersion) {
|
||||
errMessage := u.Tr.TemplateLocalize(
|
||||
"MajorVersionErr",
|
||||
i18n.Teml{
|
||||
errMessage := utils.ResolvePlaceholderString(
|
||||
u.Tr.MajorVersionErr, map[string]string{
|
||||
"newVersion": newVersion,
|
||||
"currentVersion": currentVersion,
|
||||
},
|
||||
@ -126,12 +126,12 @@ func (u *Updater) checkForNewUpdate() (string, error) {
|
||||
}
|
||||
u.Log.Info("Checking for resource at url " + rawUrl)
|
||||
if !u.verifyResourceFound(rawUrl) {
|
||||
errMessage := u.Tr.TemplateLocalize(
|
||||
"CouldNotFindBinaryErr",
|
||||
i18n.Teml{
|
||||
errMessage := utils.ResolvePlaceholderString(
|
||||
u.Tr.CouldNotFindBinaryErr, map[string]string{
|
||||
"url": rawUrl,
|
||||
},
|
||||
)
|
||||
|
||||
return "", errors.New(errMessage)
|
||||
}
|
||||
u.Log.Info("Verified resource is available, ready to update")
|
||||
|
@ -101,7 +101,8 @@ func Loader() string {
|
||||
// ResolvePlaceholderString populates a template with values
|
||||
func ResolvePlaceholderString(str string, arguments map[string]string) string {
|
||||
for key, value := range arguments {
|
||||
str = strings.Replace(str, "{{"+key+"}}", value, -1)
|
||||
str = strings.ReplaceAll(str, "{{"+key+"}}", value)
|
||||
str = strings.ReplaceAll(str, "{{."+key+"}}", value)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
@ -51,8 +51,44 @@ func writeString(file *os.File, str string) {
|
||||
}
|
||||
|
||||
func localisedTitle(mApp *app.App, str string) string {
|
||||
viewTitle := strings.Title(str) + "Title"
|
||||
return mApp.Tr.SLocalize(viewTitle)
|
||||
tr := mApp.Tr
|
||||
|
||||
contextTitleMap := map[string]string{
|
||||
"global": tr.GlobalTitle,
|
||||
"navigation": tr.NavigationTitle,
|
||||
"branches": tr.BranchesTitle,
|
||||
"localBranches": tr.LocalBranchesTitle,
|
||||
"files": tr.FilesTitle,
|
||||
"status": tr.StatusTitle,
|
||||
"submodules": tr.SubmodulesTitle,
|
||||
"subCommits": tr.SubCommitsTitle,
|
||||
"remoteBranches": tr.RemoteBranchesTitle,
|
||||
"remotes": tr.RemotesTitle,
|
||||
"reflogCommits": tr.ReflogCommitsTitle,
|
||||
"tags": tr.TagsTitle,
|
||||
"commitFiles": tr.CommitFilesTitle,
|
||||
"commitMessage": tr.CommitMessageTitle,
|
||||
"commits": tr.CommitsTitle,
|
||||
"confirmation": tr.ConfirmationTitle,
|
||||
"credentials": tr.CredentialsTitle,
|
||||
"information": tr.InformationTitle,
|
||||
"main": tr.MainTitle,
|
||||
"patchBuilding": tr.PatchBuildingTitle,
|
||||
"merging": tr.MergingTitle,
|
||||
"normal": tr.NormalTitle,
|
||||
"staging": tr.StagingTitle,
|
||||
"menu": tr.MenuTitle,
|
||||
"search": tr.SearchTitle,
|
||||
"secondary": tr.SecondaryTitle,
|
||||
"stash": tr.StashTitle,
|
||||
}
|
||||
|
||||
title, ok := contextTitleMap[str]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("title not found for %s", str))
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
func formatTitle(title string) string {
|
||||
@ -155,14 +191,14 @@ outer:
|
||||
translatedView := localisedTitle(mApp, viewName)
|
||||
var title string
|
||||
if contextAndView.subtitle == "" {
|
||||
addendum := " " + mApp.Tr.SLocalize("Panel")
|
||||
addendum := " " + mApp.Tr.Panel
|
||||
if viewName == "global" || viewName == "navigation" {
|
||||
addendum = ""
|
||||
}
|
||||
title = fmt.Sprintf("%s%s", translatedView, addendum)
|
||||
} else {
|
||||
translatedContextName := localisedTitle(mApp, contextAndView.subtitle)
|
||||
title = fmt.Sprintf("%s %s (%s)", translatedView, mApp.Tr.SLocalize("Panel"), translatedContextName)
|
||||
title = fmt.Sprintf("%s %s (%s)", translatedView, mApp.Tr.Panel, translatedContextName)
|
||||
}
|
||||
|
||||
for _, binding := range contextBindings {
|
||||
@ -194,7 +230,7 @@ func addBinding(title string, bindingSections []*bindingSection, binding *gui.Bi
|
||||
}
|
||||
|
||||
func formatSections(mApp *app.App, bindingSections []*bindingSection) string {
|
||||
content := fmt.Sprintf("# Lazygit %s\n", mApp.Tr.SLocalize("Keybindings"))
|
||||
content := fmt.Sprintf("# Lazygit %s\n", mApp.Tr.Keybindings)
|
||||
|
||||
for _, section := range bindingSections {
|
||||
content += formatTitle(section.title)
|
||||
|
19
vendor/github.com/nicksnyder/go-i18n/v2/LICENSE
generated
vendored
19
vendor/github.com/nicksnyder/go-i18n/v2/LICENSE
generated
vendored
@ -1,19 +0,0 @@
|
||||
Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
136
vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go
generated
vendored
136
vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go
generated
vendored
@ -1,136 +0,0 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/nicksnyder/go-i18n/v2/internal/plural"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// UnmarshalFunc unmarshals data into v.
|
||||
type UnmarshalFunc func(data []byte, v interface{}) error
|
||||
|
||||
// Bundle stores a set of messages and pluralization rules.
|
||||
// Most applications only need a single bundle
|
||||
// that is initialized early in the application's lifecycle.
|
||||
// It is not goroutine safe to modify the bundle while Localizers
|
||||
// are reading from it.
|
||||
type Bundle struct {
|
||||
defaultLanguage language.Tag
|
||||
unmarshalFuncs map[string]UnmarshalFunc
|
||||
messageTemplates map[language.Tag]map[string]*MessageTemplate
|
||||
pluralRules plural.Rules
|
||||
tags []language.Tag
|
||||
matcher language.Matcher
|
||||
}
|
||||
|
||||
// artTag is the language tag used for artifical languages
|
||||
// https://en.wikipedia.org/wiki/Codes_for_constructed_languages
|
||||
var artTag = language.MustParse("art")
|
||||
|
||||
// NewBundle returns a bundle with a default language and a default set of plural rules.
|
||||
func NewBundle(defaultLanguage language.Tag) *Bundle {
|
||||
b := &Bundle{
|
||||
defaultLanguage: defaultLanguage,
|
||||
pluralRules: plural.DefaultRules(),
|
||||
}
|
||||
b.pluralRules[artTag] = b.pluralRules.Rule(language.English)
|
||||
b.addTag(defaultLanguage)
|
||||
return b
|
||||
}
|
||||
|
||||
// RegisterUnmarshalFunc registers an UnmarshalFunc for format.
|
||||
func (b *Bundle) RegisterUnmarshalFunc(format string, unmarshalFunc UnmarshalFunc) {
|
||||
if b.unmarshalFuncs == nil {
|
||||
b.unmarshalFuncs = make(map[string]UnmarshalFunc)
|
||||
}
|
||||
b.unmarshalFuncs[format] = unmarshalFunc
|
||||
}
|
||||
|
||||
// LoadMessageFile loads the bytes from path
|
||||
// and then calls ParseMessageFileBytes.
|
||||
func (b *Bundle) LoadMessageFile(path string) (*MessageFile, error) {
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.ParseMessageFileBytes(buf, path)
|
||||
}
|
||||
|
||||
// MustLoadMessageFile is similar to LoadTranslationFile
|
||||
// except it panics if an error happens.
|
||||
func (b *Bundle) MustLoadMessageFile(path string) {
|
||||
if _, err := b.LoadMessageFile(path); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseMessageFileBytes parses the bytes in buf to add translations to the bundle.
|
||||
//
|
||||
// The format of the file is everything after the last ".".
|
||||
//
|
||||
// The language tag of the file is everything after the second to last "." or after the last path separator, but before the format.
|
||||
func (b *Bundle) ParseMessageFileBytes(buf []byte, path string) (*MessageFile, error) {
|
||||
messageFile, err := ParseMessageFileBytes(buf, path, b.unmarshalFuncs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := b.AddMessages(messageFile.Tag, messageFile.Messages...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return messageFile, nil
|
||||
}
|
||||
|
||||
// MustParseMessageFileBytes is similar to ParseMessageFileBytes
|
||||
// except it panics if an error happens.
|
||||
func (b *Bundle) MustParseMessageFileBytes(buf []byte, path string) {
|
||||
if _, err := b.ParseMessageFileBytes(buf, path); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// AddMessages adds messages for a language.
|
||||
// It is useful if your messages are in a format not supported by ParseMessageFileBytes.
|
||||
func (b *Bundle) AddMessages(tag language.Tag, messages ...*Message) error {
|
||||
pluralRule := b.pluralRules.Rule(tag)
|
||||
if pluralRule == nil {
|
||||
return fmt.Errorf("no plural rule registered for %s", tag)
|
||||
}
|
||||
if b.messageTemplates == nil {
|
||||
b.messageTemplates = map[language.Tag]map[string]*MessageTemplate{}
|
||||
}
|
||||
if b.messageTemplates[tag] == nil {
|
||||
b.messageTemplates[tag] = map[string]*MessageTemplate{}
|
||||
b.addTag(tag)
|
||||
}
|
||||
for _, m := range messages {
|
||||
b.messageTemplates[tag][m.ID] = NewMessageTemplate(m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustAddMessages is similar to AddMessages except it panics if an error happens.
|
||||
func (b *Bundle) MustAddMessages(tag language.Tag, messages ...*Message) {
|
||||
if err := b.AddMessages(tag, messages...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bundle) addTag(tag language.Tag) {
|
||||
for _, t := range b.tags {
|
||||
if t == tag {
|
||||
// Tag already exists
|
||||
return
|
||||
}
|
||||
}
|
||||
b.tags = append(b.tags, tag)
|
||||
b.matcher = language.NewMatcher(b.tags)
|
||||
}
|
||||
|
||||
// LanguageTags returns the list of language tags
|
||||
// of all the translations loaded into the bundle
|
||||
func (b *Bundle) LanguageTags() []language.Tag {
|
||||
return b.tags
|
||||
}
|
24
vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go
generated
vendored
24
vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
// Package i18n provides support for looking up messages
|
||||
// according to a set of locale preferences.
|
||||
//
|
||||
// Create a Bundle to use for the lifetime of your application.
|
||||
// bundle := i18n.NewBundle(language.English)
|
||||
//
|
||||
// Load translations into your bundle during initialization.
|
||||
// bundle.LoadMessageFile("en-US.yaml")
|
||||
//
|
||||
// Create a Localizer to use for a set of language preferences.
|
||||
// func(w http.ResponseWriter, r *http.Request) {
|
||||
// lang := r.FormValue("lang")
|
||||
// accept := r.Header.Get("Accept-Language")
|
||||
// localizer := i18n.NewLocalizer(bundle, lang, accept)
|
||||
// }
|
||||
//
|
||||
// Use the Localizer to lookup messages.
|
||||
// localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// DefaultMessage: &i18n.Message{
|
||||
// ID: "HelloWorld",
|
||||
// Other: "Hello World!",
|
||||
// },
|
||||
// })
|
||||
package i18n
|
234
vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go
generated
vendored
234
vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go
generated
vendored
@ -1,234 +0,0 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"text/template"
|
||||
|
||||
"github.com/nicksnyder/go-i18n/v2/internal/plural"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Localizer provides Localize and MustLocalize methods that return localized messages.
|
||||
type Localizer struct {
|
||||
// bundle contains the messages that can be returned by the Localizer.
|
||||
bundle *Bundle
|
||||
|
||||
// tags is the list of language tags that the Localizer checks
|
||||
// in order when localizing a message.
|
||||
tags []language.Tag
|
||||
}
|
||||
|
||||
// NewLocalizer returns a new Localizer that looks up messages
|
||||
// in the bundle according to the language preferences in langs.
|
||||
// It can parse Accept-Language headers as defined in http://www.ietf.org/rfc/rfc2616.txt.
|
||||
func NewLocalizer(bundle *Bundle, langs ...string) *Localizer {
|
||||
return &Localizer{
|
||||
bundle: bundle,
|
||||
tags: parseTags(langs),
|
||||
}
|
||||
}
|
||||
|
||||
func parseTags(langs []string) []language.Tag {
|
||||
tags := []language.Tag{}
|
||||
for _, lang := range langs {
|
||||
t, _, err := language.ParseAcceptLanguage(lang)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
tags = append(tags, t...)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
// LocalizeConfig configures a call to the Localize method on Localizer.
|
||||
type LocalizeConfig struct {
|
||||
// MessageID is the id of the message to lookup.
|
||||
// This field is ignored if DefaultMessage is set.
|
||||
MessageID string
|
||||
|
||||
// TemplateData is the data passed when executing the message's template.
|
||||
// If TemplateData is nil and PluralCount is not nil, then the message template
|
||||
// will be executed with data that contains the plural count.
|
||||
TemplateData interface{}
|
||||
|
||||
// PluralCount determines which plural form of the message is used.
|
||||
PluralCount interface{}
|
||||
|
||||
// DefaultMessage is used if the message is not found in any message files.
|
||||
DefaultMessage *Message
|
||||
|
||||
// Funcs is used to extend the Go template engine's built in functions
|
||||
Funcs template.FuncMap
|
||||
}
|
||||
|
||||
type invalidPluralCountErr struct {
|
||||
messageID string
|
||||
pluralCount interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *invalidPluralCountErr) Error() string {
|
||||
return fmt.Sprintf("invalid plural count %#v for message id %q: %s", e.pluralCount, e.messageID, e.err)
|
||||
}
|
||||
|
||||
// MessageNotFoundErr is returned from Localize when a message could not be found.
|
||||
type MessageNotFoundErr struct {
|
||||
messageID string
|
||||
}
|
||||
|
||||
func (e *MessageNotFoundErr) Error() string {
|
||||
return fmt.Sprintf("message %q not found", e.messageID)
|
||||
}
|
||||
|
||||
type pluralizeErr struct {
|
||||
messageID string
|
||||
tag language.Tag
|
||||
}
|
||||
|
||||
func (e *pluralizeErr) Error() string {
|
||||
return fmt.Sprintf("unable to pluralize %q because there no plural rule for %q", e.messageID, e.tag)
|
||||
}
|
||||
|
||||
type messageIDMismatchErr struct {
|
||||
messageID string
|
||||
defaultMessageID string
|
||||
}
|
||||
|
||||
func (e *messageIDMismatchErr) Error() string {
|
||||
return fmt.Sprintf("message id %q does not match default message id %q", e.messageID, e.defaultMessageID)
|
||||
}
|
||||
|
||||
// Localize returns a localized message.
|
||||
func (l *Localizer) Localize(lc *LocalizeConfig) (string, error) {
|
||||
msg, _, err := l.LocalizeWithTag(lc)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
// Localize returns a localized message.
|
||||
func (l *Localizer) LocalizeMessage(msg *Message) (string, error) {
|
||||
return l.Localize(&LocalizeConfig{
|
||||
DefaultMessage: msg,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: uncomment this (and the test) when extract has been updated to extract these call sites too.
|
||||
// Localize returns a localized message.
|
||||
// func (l *Localizer) LocalizeMessageID(messageID string) (string, error) {
|
||||
// return l.Localize(&LocalizeConfig{
|
||||
// MessageID: messageID,
|
||||
// })
|
||||
// }
|
||||
|
||||
// LocalizeWithTag returns a localized message and the language tag.
|
||||
// It may return a best effort localized message even if an error happens.
|
||||
func (l *Localizer) LocalizeWithTag(lc *LocalizeConfig) (string, language.Tag, error) {
|
||||
messageID := lc.MessageID
|
||||
if lc.DefaultMessage != nil {
|
||||
if messageID != "" && messageID != lc.DefaultMessage.ID {
|
||||
return "", language.Und, &messageIDMismatchErr{messageID: messageID, defaultMessageID: lc.DefaultMessage.ID}
|
||||
}
|
||||
messageID = lc.DefaultMessage.ID
|
||||
}
|
||||
|
||||
var operands *plural.Operands
|
||||
templateData := lc.TemplateData
|
||||
if lc.PluralCount != nil {
|
||||
var err error
|
||||
operands, err = plural.NewOperands(lc.PluralCount)
|
||||
if err != nil {
|
||||
return "", language.Und, &invalidPluralCountErr{messageID: messageID, pluralCount: lc.PluralCount, err: err}
|
||||
}
|
||||
if templateData == nil {
|
||||
templateData = map[string]interface{}{
|
||||
"PluralCount": lc.PluralCount,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tag, template := l.getTemplate(messageID, lc.DefaultMessage)
|
||||
if template == nil {
|
||||
return "", language.Und, &MessageNotFoundErr{messageID: messageID}
|
||||
}
|
||||
|
||||
pluralForm := l.pluralForm(tag, operands)
|
||||
if pluralForm == plural.Invalid {
|
||||
return "", language.Und, &pluralizeErr{messageID: messageID, tag: tag}
|
||||
}
|
||||
|
||||
msg, err := template.Execute(pluralForm, templateData, lc.Funcs)
|
||||
if err != nil {
|
||||
// Attempt to fallback to "Other" pluralization in case translations are incomplete.
|
||||
if pluralForm != plural.Other {
|
||||
msg2, err2 := template.Execute(plural.Other, templateData, lc.Funcs)
|
||||
if err2 == nil {
|
||||
return msg2, tag, err
|
||||
}
|
||||
}
|
||||
return "", language.Und, err
|
||||
}
|
||||
return msg, tag, nil
|
||||
}
|
||||
|
||||
func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Tag, *MessageTemplate) {
|
||||
// Fast path.
|
||||
// Optimistically assume this message id is defined in each language.
|
||||
fastTag, template := l.matchTemplate(id, defaultMessage, l.bundle.matcher, l.bundle.tags)
|
||||
if template != nil {
|
||||
return fastTag, template
|
||||
}
|
||||
|
||||
if len(l.bundle.tags) <= 1 {
|
||||
return l.bundle.defaultLanguage, nil
|
||||
}
|
||||
|
||||
// Slow path.
|
||||
// We didn't find a translation for the tag suggested by the default matcher
|
||||
// so we need to create a new matcher that contains only the tags in the bundle
|
||||
// that have this message.
|
||||
foundTags := make([]language.Tag, 0, len(l.bundle.messageTemplates)+1)
|
||||
foundTags = append(foundTags, l.bundle.defaultLanguage)
|
||||
|
||||
for t, templates := range l.bundle.messageTemplates {
|
||||
template := templates[id]
|
||||
if template == nil || template.Other == "" {
|
||||
continue
|
||||
}
|
||||
foundTags = append(foundTags, t)
|
||||
}
|
||||
|
||||
return l.matchTemplate(id, defaultMessage, language.NewMatcher(foundTags), foundTags)
|
||||
}
|
||||
|
||||
func (l *Localizer) matchTemplate(id string, defaultMessage *Message, matcher language.Matcher, tags []language.Tag) (language.Tag, *MessageTemplate) {
|
||||
_, i, _ := matcher.Match(l.tags...)
|
||||
tag := tags[i]
|
||||
templates := l.bundle.messageTemplates[tag]
|
||||
if templates != nil && templates[id] != nil {
|
||||
return tag, templates[id]
|
||||
}
|
||||
if tag == l.bundle.defaultLanguage && defaultMessage != nil {
|
||||
return tag, NewMessageTemplate(defaultMessage)
|
||||
}
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
func (l *Localizer) pluralForm(tag language.Tag, operands *plural.Operands) plural.Form {
|
||||
if operands == nil {
|
||||
return plural.Other
|
||||
}
|
||||
pluralRule := l.bundle.pluralRules.Rule(tag)
|
||||
if pluralRule == nil {
|
||||
return plural.Invalid
|
||||
}
|
||||
return pluralRule.PluralFormFunc(operands)
|
||||
}
|
||||
|
||||
// MustLocalize is similar to Localize, except it panics if an error happens.
|
||||
func (l *Localizer) MustLocalize(lc *LocalizeConfig) string {
|
||||
localized, err := l.Localize(lc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return localized
|
||||
}
|
221
vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go
generated
vendored
221
vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go
generated
vendored
@ -1,221 +0,0 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Message is a string that can be localized.
|
||||
type Message struct {
|
||||
// ID uniquely identifies the message.
|
||||
ID string
|
||||
|
||||
// Hash uniquely identifies the content of the message
|
||||
// that this message was translated from.
|
||||
Hash string
|
||||
|
||||
// Description describes the message to give additional
|
||||
// context to translators that may be relevant for translation.
|
||||
Description string
|
||||
|
||||
// LeftDelim is the left Go template delimiter.
|
||||
LeftDelim string
|
||||
|
||||
// RightDelim is the right Go template delimiter.``
|
||||
RightDelim string
|
||||
|
||||
// Zero is the content of the message for the CLDR plural form "zero".
|
||||
Zero string
|
||||
|
||||
// One is the content of the message for the CLDR plural form "one".
|
||||
One string
|
||||
|
||||
// Two is the content of the message for the CLDR plural form "two".
|
||||
Two string
|
||||
|
||||
// Few is the content of the message for the CLDR plural form "few".
|
||||
Few string
|
||||
|
||||
// Many is the content of the message for the CLDR plural form "many".
|
||||
Many string
|
||||
|
||||
// Other is the content of the message for the CLDR plural form "other".
|
||||
Other string
|
||||
}
|
||||
|
||||
// NewMessage parses data and returns a new message.
|
||||
func NewMessage(data interface{}) (*Message, error) {
|
||||
m := &Message{}
|
||||
if err := m.unmarshalInterface(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// MustNewMessage is similar to NewMessage except it panics if an error happens.
|
||||
func MustNewMessage(data interface{}) *Message {
|
||||
m, err := NewMessage(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// unmarshalInterface unmarshals a message from data.
|
||||
func (m *Message) unmarshalInterface(v interface{}) error {
|
||||
strdata, err := stringMap(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range strdata {
|
||||
switch strings.ToLower(k) {
|
||||
case "id":
|
||||
m.ID = v
|
||||
case "description":
|
||||
m.Description = v
|
||||
case "hash":
|
||||
m.Hash = v
|
||||
case "leftdelim":
|
||||
m.LeftDelim = v
|
||||
case "rightdelim":
|
||||
m.RightDelim = v
|
||||
case "zero":
|
||||
m.Zero = v
|
||||
case "one":
|
||||
m.One = v
|
||||
case "two":
|
||||
m.Two = v
|
||||
case "few":
|
||||
m.Few = v
|
||||
case "many":
|
||||
m.Many = v
|
||||
case "other":
|
||||
m.Other = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type keyTypeErr struct {
|
||||
key interface{}
|
||||
}
|
||||
|
||||
func (err *keyTypeErr) Error() string {
|
||||
return fmt.Sprintf("expected key to be a string but got %#v", err.key)
|
||||
}
|
||||
|
||||
type valueTypeErr struct {
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (err *valueTypeErr) Error() string {
|
||||
return fmt.Sprintf("unsupported type %#v", err.value)
|
||||
}
|
||||
|
||||
func stringMap(v interface{}) (map[string]string, error) {
|
||||
switch value := v.(type) {
|
||||
case string:
|
||||
return map[string]string{
|
||||
"other": value,
|
||||
}, nil
|
||||
case map[string]string:
|
||||
return value, nil
|
||||
case map[string]interface{}:
|
||||
strdata := make(map[string]string, len(value))
|
||||
for k, v := range value {
|
||||
err := stringSubmap(k, v, strdata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return strdata, nil
|
||||
case map[interface{}]interface{}:
|
||||
strdata := make(map[string]string, len(value))
|
||||
for k, v := range value {
|
||||
kstr, ok := k.(string)
|
||||
if !ok {
|
||||
return nil, &keyTypeErr{key: k}
|
||||
}
|
||||
err := stringSubmap(kstr, v, strdata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return strdata, nil
|
||||
default:
|
||||
return nil, &valueTypeErr{value: value}
|
||||
}
|
||||
}
|
||||
|
||||
func stringSubmap(k string, v interface{}, strdata map[string]string) error {
|
||||
if k == "translation" {
|
||||
switch vt := v.(type) {
|
||||
case string:
|
||||
strdata["other"] = vt
|
||||
default:
|
||||
v1Message, err := stringMap(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for kk, vv := range v1Message {
|
||||
strdata[kk] = vv
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch vt := v.(type) {
|
||||
case string:
|
||||
strdata[k] = vt
|
||||
return nil
|
||||
case nil:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("expected value for key %q be a string but got %#v", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// isMessage tells whether the given data is a message, or a map containing
|
||||
// nested messages.
|
||||
// A map is assumed to be a message if it contains any of the "reserved" keys:
|
||||
// "id", "description", "hash", "leftdelim", "rightdelim", "zero", "one", "two", "few", "many", "other"
|
||||
// with a string value.
|
||||
// e.g.,
|
||||
// - {"message": {"description": "world"}} is a message
|
||||
// - {"message": {"description": "world", "foo": "bar"}} is a message ("foo" key is ignored)
|
||||
// - {"notmessage": {"description": {"hello": "world"}}} is not
|
||||
// - {"notmessage": {"foo": "bar"}} is not
|
||||
func isMessage(v interface{}) bool {
|
||||
reservedKeys := []string{"id", "description", "hash", "leftdelim", "rightdelim", "zero", "one", "two", "few", "many", "other"}
|
||||
switch data := v.(type) {
|
||||
case string:
|
||||
return true
|
||||
case map[string]interface{}:
|
||||
for _, key := range reservedKeys {
|
||||
val, ok := data[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
_, ok = val.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// v is a message if it contains a "reserved" key holding a string value
|
||||
return true
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
for _, key := range reservedKeys {
|
||||
val, ok := data[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
_, ok = val.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// v is a message if it contains a "reserved" key holding a string value
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
65
vendor/github.com/nicksnyder/go-i18n/v2/i18n/message_template.go
generated
vendored
65
vendor/github.com/nicksnyder/go-i18n/v2/i18n/message_template.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"text/template"
|
||||
|
||||
"github.com/nicksnyder/go-i18n/v2/internal"
|
||||
"github.com/nicksnyder/go-i18n/v2/internal/plural"
|
||||
)
|
||||
|
||||
// MessageTemplate is an executable template for a message.
|
||||
type MessageTemplate struct {
|
||||
*Message
|
||||
PluralTemplates map[plural.Form]*internal.Template
|
||||
}
|
||||
|
||||
// NewMessageTemplate returns a new message template.
|
||||
func NewMessageTemplate(m *Message) *MessageTemplate {
|
||||
pluralTemplates := map[plural.Form]*internal.Template{}
|
||||
setPluralTemplate(pluralTemplates, plural.Zero, m.Zero, m.LeftDelim, m.RightDelim)
|
||||
setPluralTemplate(pluralTemplates, plural.One, m.One, m.LeftDelim, m.RightDelim)
|
||||
setPluralTemplate(pluralTemplates, plural.Two, m.Two, m.LeftDelim, m.RightDelim)
|
||||
setPluralTemplate(pluralTemplates, plural.Few, m.Few, m.LeftDelim, m.RightDelim)
|
||||
setPluralTemplate(pluralTemplates, plural.Many, m.Many, m.LeftDelim, m.RightDelim)
|
||||
setPluralTemplate(pluralTemplates, plural.Other, m.Other, m.LeftDelim, m.RightDelim)
|
||||
if len(pluralTemplates) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &MessageTemplate{
|
||||
Message: m,
|
||||
PluralTemplates: pluralTemplates,
|
||||
}
|
||||
}
|
||||
|
||||
func setPluralTemplate(pluralTemplates map[plural.Form]*internal.Template, pluralForm plural.Form, src, leftDelim, rightDelim string) {
|
||||
if src != "" {
|
||||
pluralTemplates[pluralForm] = &internal.Template{
|
||||
Src: src,
|
||||
LeftDelim: leftDelim,
|
||||
RightDelim: rightDelim,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type pluralFormNotFoundError struct {
|
||||
pluralForm plural.Form
|
||||
messageID string
|
||||
}
|
||||
|
||||
func (e pluralFormNotFoundError) Error() string {
|
||||
return fmt.Sprintf("message %q has no plural form %q", e.messageID, e.pluralForm)
|
||||
}
|
||||
|
||||
// Execute executes the template for the plural form and template data.
|
||||
func (mt *MessageTemplate) Execute(pluralForm plural.Form, data interface{}, funcs template.FuncMap) (string, error) {
|
||||
t := mt.PluralTemplates[pluralForm]
|
||||
if t == nil {
|
||||
return "", pluralFormNotFoundError{
|
||||
pluralForm: pluralForm,
|
||||
messageID: mt.Message.ID,
|
||||
}
|
||||
}
|
||||
return t.Execute(funcs, data)
|
||||
}
|
166
vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go
generated
vendored
166
vendor/github.com/nicksnyder/go-i18n/v2/i18n/parse.go
generated
vendored
@ -1,166 +0,0 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// MessageFile represents a parsed message file.
|
||||
type MessageFile struct {
|
||||
Path string
|
||||
Tag language.Tag
|
||||
Format string
|
||||
Messages []*Message
|
||||
}
|
||||
|
||||
// ParseMessageFileBytes returns the messages parsed from file.
|
||||
func ParseMessageFileBytes(buf []byte, path string, unmarshalFuncs map[string]UnmarshalFunc) (*MessageFile, error) {
|
||||
lang, format := parsePath(path)
|
||||
tag := language.Make(lang)
|
||||
messageFile := &MessageFile{
|
||||
Path: path,
|
||||
Tag: tag,
|
||||
Format: format,
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return messageFile, nil
|
||||
}
|
||||
unmarshalFunc := unmarshalFuncs[messageFile.Format]
|
||||
if unmarshalFunc == nil {
|
||||
if messageFile.Format == "json" {
|
||||
unmarshalFunc = json.Unmarshal
|
||||
} else {
|
||||
return nil, fmt.Errorf("no unmarshaler registered for %s", messageFile.Format)
|
||||
}
|
||||
}
|
||||
var err error
|
||||
var raw interface{}
|
||||
if err = unmarshalFunc(buf, &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if messageFile.Messages, err = recGetMessages(raw, isMessage(raw), true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return messageFile, nil
|
||||
}
|
||||
|
||||
const nestedSeparator = "."
|
||||
|
||||
var errInvalidTranslationFile = errors.New("invalid translation file, expected key-values, got a single value")
|
||||
|
||||
// recGetMessages looks for translation messages inside "raw" parameter,
|
||||
// scanning nested maps using recursion.
|
||||
func recGetMessages(raw interface{}, isMapMessage, isInitialCall bool) ([]*Message, error) {
|
||||
var messages []*Message
|
||||
var err error
|
||||
|
||||
switch data := raw.(type) {
|
||||
case string:
|
||||
if isInitialCall {
|
||||
return nil, errInvalidTranslationFile
|
||||
}
|
||||
m, err := NewMessage(data)
|
||||
return []*Message{m}, err
|
||||
|
||||
case map[string]interface{}:
|
||||
if isMapMessage {
|
||||
m, err := NewMessage(data)
|
||||
return []*Message{m}, err
|
||||
}
|
||||
messages = make([]*Message, 0, len(data))
|
||||
for id, data := range data {
|
||||
// recursively scan map items
|
||||
messages, err = addChildMessages(id, data, messages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
case map[interface{}]interface{}:
|
||||
if isMapMessage {
|
||||
m, err := NewMessage(data)
|
||||
return []*Message{m}, err
|
||||
}
|
||||
messages = make([]*Message, 0, len(data))
|
||||
for id, data := range data {
|
||||
strid, ok := id.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected key to be string but got %#v", id)
|
||||
}
|
||||
// recursively scan map items
|
||||
messages, err = addChildMessages(strid, data, messages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
case []interface{}:
|
||||
// Backward compatibility for v1 file format.
|
||||
messages = make([]*Message, 0, len(data))
|
||||
for _, data := range data {
|
||||
// recursively scan slice items
|
||||
childMessages, err := recGetMessages(data, isMessage(data), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
messages = append(messages, childMessages...)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported file format %T", raw)
|
||||
}
|
||||
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func addChildMessages(id string, data interface{}, messages []*Message) ([]*Message, error) {
|
||||
isChildMessage := isMessage(data)
|
||||
childMessages, err := recGetMessages(data, isChildMessage, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range childMessages {
|
||||
if isChildMessage {
|
||||
if m.ID == "" {
|
||||
m.ID = id // start with innermost key
|
||||
}
|
||||
} else {
|
||||
m.ID = id + nestedSeparator + m.ID // update ID with each nested key on the way
|
||||
}
|
||||
messages = append(messages, m)
|
||||
}
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func parsePath(path string) (langTag, format string) {
|
||||
formatStartIdx := -1
|
||||
for i := len(path) - 1; i >= 0; i-- {
|
||||
c := path[i]
|
||||
if os.IsPathSeparator(c) {
|
||||
if formatStartIdx != -1 {
|
||||
langTag = path[i+1 : formatStartIdx]
|
||||
}
|
||||
return
|
||||
}
|
||||
if path[i] == '.' {
|
||||
if formatStartIdx != -1 {
|
||||
langTag = path[i+1 : formatStartIdx]
|
||||
return
|
||||
}
|
||||
if formatStartIdx == -1 {
|
||||
format = path[i+1:]
|
||||
formatStartIdx = i
|
||||
}
|
||||
}
|
||||
}
|
||||
if formatStartIdx != -1 {
|
||||
langTag = path[:formatStartIdx]
|
||||
}
|
||||
return
|
||||
}
|
3
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go
generated
vendored
3
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go
generated
vendored
@ -1,3 +0,0 @@
|
||||
// Package plural provides support for pluralizing messages
|
||||
// according to CLDR rules http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
package plural
|
16
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go
generated
vendored
16
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
package plural
|
||||
|
||||
// Form represents a language pluralization form as defined here:
|
||||
// http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
type Form string
|
||||
|
||||
// All defined plural forms.
|
||||
const (
|
||||
Invalid Form = ""
|
||||
Zero Form = "zero"
|
||||
One Form = "one"
|
||||
Two Form = "two"
|
||||
Few Form = "few"
|
||||
Many Form = "many"
|
||||
Other Form = "other"
|
||||
)
|
120
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go
generated
vendored
120
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go
generated
vendored
@ -1,120 +0,0 @@
|
||||
package plural
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Operands is a representation of http://unicode.org/reports/tr35/tr35-numbers.html#Operands
|
||||
type Operands struct {
|
||||
N float64 // absolute value of the source number (integer and decimals)
|
||||
I int64 // integer digits of n
|
||||
V int64 // number of visible fraction digits in n, with trailing zeros
|
||||
W int64 // number of visible fraction digits in n, without trailing zeros
|
||||
F int64 // visible fractional digits in n, with trailing zeros
|
||||
T int64 // visible fractional digits in n, without trailing zeros
|
||||
}
|
||||
|
||||
// NEqualsAny returns true if o represents an integer equal to any of the arguments.
|
||||
func (o *Operands) NEqualsAny(any ...int64) bool {
|
||||
for _, i := range any {
|
||||
if o.I == i && o.T == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NModEqualsAny returns true if o represents an integer equal to any of the arguments modulo mod.
|
||||
func (o *Operands) NModEqualsAny(mod int64, any ...int64) bool {
|
||||
modI := o.I % mod
|
||||
for _, i := range any {
|
||||
if modI == i && o.T == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NInRange returns true if o represents an integer in the closed interval [from, to].
|
||||
func (o *Operands) NInRange(from, to int64) bool {
|
||||
return o.T == 0 && from <= o.I && o.I <= to
|
||||
}
|
||||
|
||||
// NModInRange returns true if o represents an integer in the closed interval [from, to] modulo mod.
|
||||
func (o *Operands) NModInRange(mod, from, to int64) bool {
|
||||
modI := o.I % mod
|
||||
return o.T == 0 && from <= modI && modI <= to
|
||||
}
|
||||
|
||||
// NewOperands returns the operands for number.
|
||||
func NewOperands(number interface{}) (*Operands, error) {
|
||||
switch number := number.(type) {
|
||||
case int:
|
||||
return newOperandsInt64(int64(number)), nil
|
||||
case int8:
|
||||
return newOperandsInt64(int64(number)), nil
|
||||
case int16:
|
||||
return newOperandsInt64(int64(number)), nil
|
||||
case int32:
|
||||
return newOperandsInt64(int64(number)), nil
|
||||
case int64:
|
||||
return newOperandsInt64(number), nil
|
||||
case string:
|
||||
return newOperandsString(number)
|
||||
case float32, float64:
|
||||
return nil, fmt.Errorf("floats should be formatted into a string")
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid type %T; expected integer or string", number)
|
||||
}
|
||||
}
|
||||
|
||||
func newOperandsInt64(i int64) *Operands {
|
||||
if i < 0 {
|
||||
i = -i
|
||||
}
|
||||
return &Operands{float64(i), i, 0, 0, 0, 0}
|
||||
}
|
||||
|
||||
func newOperandsString(s string) (*Operands, error) {
|
||||
if s[0] == '-' {
|
||||
s = s[1:]
|
||||
}
|
||||
n, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops := &Operands{N: n}
|
||||
parts := strings.SplitN(s, ".", 2)
|
||||
ops.I, err = strconv.ParseInt(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(parts) == 1 {
|
||||
return ops, nil
|
||||
}
|
||||
fraction := parts[1]
|
||||
ops.V = int64(len(fraction))
|
||||
for i := ops.V - 1; i >= 0; i-- {
|
||||
if fraction[i] != '0' {
|
||||
ops.W = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
if ops.V > 0 {
|
||||
f, err := strconv.ParseInt(fraction, 10, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops.F = f
|
||||
}
|
||||
if ops.W > 0 {
|
||||
t, err := strconv.ParseInt(fraction[:ops.W], 10, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops.T = t
|
||||
}
|
||||
return ops, nil
|
||||
}
|
44
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule.go
generated
vendored
44
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
package plural
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Rule defines the CLDR plural rules for a language.
|
||||
// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
|
||||
// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
|
||||
type Rule struct {
|
||||
PluralForms map[Form]struct{}
|
||||
PluralFormFunc func(*Operands) Form
|
||||
}
|
||||
|
||||
func addPluralRules(rules Rules, ids []string, ps *Rule) {
|
||||
for _, id := range ids {
|
||||
if id == "root" {
|
||||
continue
|
||||
}
|
||||
tag := language.MustParse(id)
|
||||
rules[tag] = ps
|
||||
}
|
||||
}
|
||||
|
||||
func newPluralFormSet(pluralForms ...Form) map[Form]struct{} {
|
||||
set := make(map[Form]struct{}, len(pluralForms))
|
||||
for _, plural := range pluralForms {
|
||||
set[plural] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func intInRange(i, from, to int64) bool {
|
||||
return from <= i && i <= to
|
||||
}
|
||||
|
||||
func intEqualsAny(i int64, any ...int64) bool {
|
||||
for _, a := range any {
|
||||
if i == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
589
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule_gen.go
generated
vendored
589
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule_gen.go
generated
vendored
@ -1,589 +0,0 @@
|
||||
// This file is generated by i18n/plural/codegen/generate.sh; DO NOT EDIT
|
||||
|
||||
package plural
|
||||
|
||||
// DefaultRules returns a map of Rules generated from CLDR language data.
|
||||
func DefaultRules() Rules {
|
||||
rules := Rules{}
|
||||
|
||||
addPluralRules(rules, []string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "root", "sah", "ses", "sg", "th", "to", "vi", "wo", "yo", "yue", "zh"}, &Rule{
|
||||
PluralForms: newPluralFormSet(Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"am", "as", "bn", "fa", "gu", "hi", "kn", "zu"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 0 or n = 1
|
||||
if intEqualsAny(ops.I, 0) ||
|
||||
ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ff", "fr", "hy", "kab"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 0,1
|
||||
if intEqualsAny(ops.I, 0, 1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"pt"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 0..1
|
||||
if intInRange(ops.I, 0, 1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "ia", "io", "it", "ji", "nl", "pt_PT", "sc", "scn", "sv", "sw", "ur", "yi"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 1 and v = 0
|
||||
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"si"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0,1 or i = 0 and f = 1
|
||||
if ops.NEqualsAny(0, 1) ||
|
||||
intEqualsAny(ops.I, 0) && intEqualsAny(ops.F, 1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ak", "bh", "guw", "ln", "mg", "nso", "pa", "ti", "wa"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0..1
|
||||
if ops.NInRange(0, 1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"tzm"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0..1 or n = 11..99
|
||||
if ops.NInRange(0, 1) ||
|
||||
ops.NInRange(11, 99) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"af", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "mr", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sd", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"da"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 1 or t != 0 and i = 0,1
|
||||
if ops.NEqualsAny(1) ||
|
||||
!intEqualsAny(ops.T, 0) && intEqualsAny(ops.I, 0, 1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"is"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0
|
||||
if intEqualsAny(ops.T, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
|
||||
!intEqualsAny(ops.T, 0) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"mk"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
|
||||
intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ceb", "fil", "tl"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I, 1, 2, 3) ||
|
||||
intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I%10, 4, 6, 9) ||
|
||||
!intEqualsAny(ops.V, 0) && !intEqualsAny(ops.F%10, 4, 6, 9) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"lv", "prg"}, &Rule{
|
||||
PluralForms: newPluralFormSet(Zero, One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19
|
||||
if ops.NModEqualsAny(10, 0) ||
|
||||
ops.NModInRange(100, 11, 19) ||
|
||||
intEqualsAny(ops.V, 2) && intInRange(ops.F%100, 11, 19) {
|
||||
return Zero
|
||||
}
|
||||
// n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1
|
||||
if ops.NModEqualsAny(10, 1) && !ops.NModEqualsAny(100, 11) ||
|
||||
intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) ||
|
||||
!intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"lag"}, &Rule{
|
||||
PluralForms: newPluralFormSet(Zero, One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0
|
||||
if ops.NEqualsAny(0) {
|
||||
return Zero
|
||||
}
|
||||
// i = 0,1 and n != 0
|
||||
if intEqualsAny(ops.I, 0, 1) && !ops.NEqualsAny(0) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ksh"}, &Rule{
|
||||
PluralForms: newPluralFormSet(Zero, One, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0
|
||||
if ops.NEqualsAny(0) {
|
||||
return Zero
|
||||
}
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"iu", "naq", "se", "sma", "smi", "smj", "smn", "sms"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
// n = 2
|
||||
if ops.NEqualsAny(2) {
|
||||
return Two
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"shi"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 0 or n = 1
|
||||
if intEqualsAny(ops.I, 0) ||
|
||||
ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
// n = 2..10
|
||||
if ops.NInRange(2, 10) {
|
||||
return Few
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"mo", "ro"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 1 and v = 0
|
||||
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||
return One
|
||||
}
|
||||
// v != 0 or n = 0 or n % 100 = 2..19
|
||||
if !intEqualsAny(ops.V, 0) ||
|
||||
ops.NEqualsAny(0) ||
|
||||
ops.NModInRange(100, 2, 19) {
|
||||
return Few
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"bs", "hr", "sh", "sr"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
|
||||
intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) {
|
||||
return One
|
||||
}
|
||||
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14
|
||||
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) ||
|
||||
intInRange(ops.F%10, 2, 4) && !intInRange(ops.F%100, 12, 14) {
|
||||
return Few
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"gd"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Few, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 1,11
|
||||
if ops.NEqualsAny(1, 11) {
|
||||
return One
|
||||
}
|
||||
// n = 2,12
|
||||
if ops.NEqualsAny(2, 12) {
|
||||
return Two
|
||||
}
|
||||
// n = 3..10,13..19
|
||||
if ops.NInRange(3, 10) || ops.NInRange(13, 19) {
|
||||
return Few
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"sl"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Few, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// v = 0 and i % 100 = 1
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) {
|
||||
return One
|
||||
}
|
||||
// v = 0 and i % 100 = 2
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) {
|
||||
return Two
|
||||
}
|
||||
// v = 0 and i % 100 = 3..4 or v != 0
|
||||
if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
|
||||
!intEqualsAny(ops.V, 0) {
|
||||
return Few
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"dsb", "hsb"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Few, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// v = 0 and i % 100 = 1 or f % 100 = 1
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) ||
|
||||
intEqualsAny(ops.F%100, 1) {
|
||||
return One
|
||||
}
|
||||
// v = 0 and i % 100 = 2 or f % 100 = 2
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) ||
|
||||
intEqualsAny(ops.F%100, 2) {
|
||||
return Two
|
||||
}
|
||||
// v = 0 and i % 100 = 3..4 or f % 100 = 3..4
|
||||
if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
|
||||
intInRange(ops.F%100, 3, 4) {
|
||||
return Few
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"he", "iw"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 1 and v = 0
|
||||
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||
return One
|
||||
}
|
||||
// i = 2 and v = 0
|
||||
if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) {
|
||||
return Two
|
||||
}
|
||||
// v = 0 and n != 0..10 and n % 10 = 0
|
||||
if intEqualsAny(ops.V, 0) && !ops.NInRange(0, 10) && ops.NModEqualsAny(10, 0) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"cs", "sk"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 1 and v = 0
|
||||
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||
return One
|
||||
}
|
||||
// i = 2..4 and v = 0
|
||||
if intInRange(ops.I, 2, 4) && intEqualsAny(ops.V, 0) {
|
||||
return Few
|
||||
}
|
||||
// v != 0
|
||||
if !intEqualsAny(ops.V, 0) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"pl"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// i = 1 and v = 0
|
||||
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||
return One
|
||||
}
|
||||
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14
|
||||
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
|
||||
return Few
|
||||
}
|
||||
// v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14
|
||||
if intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I, 1) && intInRange(ops.I%10, 0, 1) ||
|
||||
intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
|
||||
intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 12, 14) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"be"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n % 10 = 1 and n % 100 != 11
|
||||
if ops.NModEqualsAny(10, 1) && !ops.NModEqualsAny(100, 11) {
|
||||
return One
|
||||
}
|
||||
// n % 10 = 2..4 and n % 100 != 12..14
|
||||
if ops.NModInRange(10, 2, 4) && !ops.NModInRange(100, 12, 14) {
|
||||
return Few
|
||||
}
|
||||
// n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14
|
||||
if ops.NModEqualsAny(10, 0) ||
|
||||
ops.NModInRange(10, 5, 9) ||
|
||||
ops.NModInRange(100, 11, 14) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"lt"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n % 10 = 1 and n % 100 != 11..19
|
||||
if ops.NModEqualsAny(10, 1) && !ops.NModInRange(100, 11, 19) {
|
||||
return One
|
||||
}
|
||||
// n % 10 = 2..9 and n % 100 != 11..19
|
||||
if ops.NModInRange(10, 2, 9) && !ops.NModInRange(100, 11, 19) {
|
||||
return Few
|
||||
}
|
||||
// f != 0
|
||||
if !intEqualsAny(ops.F, 0) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"mt"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
// n = 0 or n % 100 = 2..10
|
||||
if ops.NEqualsAny(0) ||
|
||||
ops.NModInRange(100, 2, 10) {
|
||||
return Few
|
||||
}
|
||||
// n % 100 = 11..19
|
||||
if ops.NModInRange(100, 11, 19) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ru", "uk"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// v = 0 and i % 10 = 1 and i % 100 != 11
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) {
|
||||
return One
|
||||
}
|
||||
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14
|
||||
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
|
||||
return Few
|
||||
}
|
||||
// v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 0) ||
|
||||
intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
|
||||
intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 11, 14) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"br"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n % 10 = 1 and n % 100 != 11,71,91
|
||||
if ops.NModEqualsAny(10, 1) && !ops.NModEqualsAny(100, 11, 71, 91) {
|
||||
return One
|
||||
}
|
||||
// n % 10 = 2 and n % 100 != 12,72,92
|
||||
if ops.NModEqualsAny(10, 2) && !ops.NModEqualsAny(100, 12, 72, 92) {
|
||||
return Two
|
||||
}
|
||||
// n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99
|
||||
if (ops.NModInRange(10, 3, 4) || ops.NModEqualsAny(10, 9)) && !(ops.NModInRange(100, 10, 19) || ops.NModInRange(100, 70, 79) || ops.NModInRange(100, 90, 99)) {
|
||||
return Few
|
||||
}
|
||||
// n != 0 and n % 1000000 = 0
|
||||
if !ops.NEqualsAny(0) && ops.NModEqualsAny(1000000, 0) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ga"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
// n = 2
|
||||
if ops.NEqualsAny(2) {
|
||||
return Two
|
||||
}
|
||||
// n = 3..6
|
||||
if ops.NInRange(3, 6) {
|
||||
return Few
|
||||
}
|
||||
// n = 7..10
|
||||
if ops.NInRange(7, 10) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"gv"}, &Rule{
|
||||
PluralForms: newPluralFormSet(One, Two, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// v = 0 and i % 10 = 1
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) {
|
||||
return One
|
||||
}
|
||||
// v = 0 and i % 10 = 2
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 2) {
|
||||
return Two
|
||||
}
|
||||
// v = 0 and i % 100 = 0,20,40,60,80
|
||||
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 0, 20, 40, 60, 80) {
|
||||
return Few
|
||||
}
|
||||
// v != 0
|
||||
if !intEqualsAny(ops.V, 0) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"ar", "ars"}, &Rule{
|
||||
PluralForms: newPluralFormSet(Zero, One, Two, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0
|
||||
if ops.NEqualsAny(0) {
|
||||
return Zero
|
||||
}
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
// n = 2
|
||||
if ops.NEqualsAny(2) {
|
||||
return Two
|
||||
}
|
||||
// n % 100 = 3..10
|
||||
if ops.NModInRange(100, 3, 10) {
|
||||
return Few
|
||||
}
|
||||
// n % 100 = 11..99
|
||||
if ops.NModInRange(100, 11, 99) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"cy"}, &Rule{
|
||||
PluralForms: newPluralFormSet(Zero, One, Two, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0
|
||||
if ops.NEqualsAny(0) {
|
||||
return Zero
|
||||
}
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
// n = 2
|
||||
if ops.NEqualsAny(2) {
|
||||
return Two
|
||||
}
|
||||
// n = 3
|
||||
if ops.NEqualsAny(3) {
|
||||
return Few
|
||||
}
|
||||
// n = 6
|
||||
if ops.NEqualsAny(6) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
addPluralRules(rules, []string{"kw"}, &Rule{
|
||||
PluralForms: newPluralFormSet(Zero, One, Two, Few, Many, Other),
|
||||
PluralFormFunc: func(ops *Operands) Form {
|
||||
// n = 0
|
||||
if ops.NEqualsAny(0) {
|
||||
return Zero
|
||||
}
|
||||
// n = 1
|
||||
if ops.NEqualsAny(1) {
|
||||
return One
|
||||
}
|
||||
// n % 100 = 2,22,42,62,82 or n%1000 = 0 and n%100000=1000..20000,40000,60000,80000 or n!=0 and n%1000000=100000
|
||||
if ops.NModEqualsAny(100, 2, 22, 42, 62, 82) ||
|
||||
ops.NModEqualsAny(1000, 0) && (ops.NModInRange(100000, 1000, 20000) || ops.NModEqualsAny(100000, 40000, 60000, 80000)) ||
|
||||
!ops.NEqualsAny(0) && ops.NModEqualsAny(1000000, 100000) {
|
||||
return Two
|
||||
}
|
||||
// n % 100 = 3,23,43,63,83
|
||||
if ops.NModEqualsAny(100, 3, 23, 43, 63, 83) {
|
||||
return Few
|
||||
}
|
||||
// n != 1 and n % 100 = 1,21,41,61,81
|
||||
if !ops.NEqualsAny(1) && ops.NModEqualsAny(100, 1, 21, 41, 61, 81) {
|
||||
return Many
|
||||
}
|
||||
return Other
|
||||
},
|
||||
})
|
||||
|
||||
return rules
|
||||
}
|
24
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rules.go
generated
vendored
24
vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rules.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
package plural
|
||||
|
||||
import "golang.org/x/text/language"
|
||||
|
||||
// Rules is a set of plural rules by language tag.
|
||||
type Rules map[language.Tag]*Rule
|
||||
|
||||
// Rule returns the closest matching plural rule for the language tag
|
||||
// or nil if no rule could be found.
|
||||
func (r Rules) Rule(tag language.Tag) *Rule {
|
||||
t := tag
|
||||
for {
|
||||
if rule := r[t]; rule != nil {
|
||||
return rule
|
||||
}
|
||||
t = t.Parent()
|
||||
if t.IsRoot() {
|
||||
break
|
||||
}
|
||||
}
|
||||
base, _ := tag.Base()
|
||||
baseTag, _ := language.Parse(base.String())
|
||||
return r[baseTag]
|
||||
}
|
51
vendor/github.com/nicksnyder/go-i18n/v2/internal/template.go
generated
vendored
51
vendor/github.com/nicksnyder/go-i18n/v2/internal/template.go
generated
vendored
@ -1,51 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"sync"
|
||||
gotemplate "text/template"
|
||||
)
|
||||
|
||||
// Template stores the template for a string.
|
||||
type Template struct {
|
||||
Src string
|
||||
LeftDelim string
|
||||
RightDelim string
|
||||
|
||||
parseOnce sync.Once
|
||||
parsedTemplate *gotemplate.Template
|
||||
parseError error
|
||||
}
|
||||
|
||||
func (t *Template) Execute(funcs gotemplate.FuncMap, data interface{}) (string, error) {
|
||||
leftDelim := t.LeftDelim
|
||||
if leftDelim == "" {
|
||||
leftDelim = "{{"
|
||||
}
|
||||
if !strings.Contains(t.Src, leftDelim) {
|
||||
// Fast path to avoid parsing a template that has no actions.
|
||||
return t.Src, nil
|
||||
}
|
||||
|
||||
var gt *gotemplate.Template
|
||||
var err error
|
||||
if funcs == nil {
|
||||
t.parseOnce.Do(func() {
|
||||
// If funcs is nil, then we only need to parse this template once.
|
||||
t.parsedTemplate, t.parseError = gotemplate.New("").Delims(t.LeftDelim, t.RightDelim).Parse(t.Src)
|
||||
})
|
||||
gt, err = t.parsedTemplate, t.parseError
|
||||
} else {
|
||||
gt, err = gotemplate.New("").Delims(t.LeftDelim, t.RightDelim).Funcs(funcs).Parse(t.Src)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := gt.Execute(&buf, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
3
vendor/golang.org/x/text/AUTHORS
generated
vendored
3
vendor/golang.org/x/text/AUTHORS
generated
vendored
@ -1,3 +0,0 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
3
vendor/golang.org/x/text/CONTRIBUTORS
generated
vendored
3
vendor/golang.org/x/text/CONTRIBUTORS
generated
vendored
@ -1,3 +0,0 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
27
vendor/golang.org/x/text/LICENSE
generated
vendored
27
vendor/golang.org/x/text/LICENSE
generated
vendored
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
vendor/golang.org/x/text/PATENTS
generated
vendored
22
vendor/golang.org/x/text/PATENTS
generated
vendored
@ -1,22 +0,0 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
16
vendor/golang.org/x/text/internal/language/common.go
generated
vendored
16
vendor/golang.org/x/text/internal/language/common.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
package language
|
||||
|
||||
// This file contains code common to the maketables.go and the package code.
|
||||
|
||||
// AliasType is the type of an alias in AliasMap.
|
||||
type AliasType int8
|
||||
|
||||
const (
|
||||
Deprecated AliasType = iota
|
||||
Macro
|
||||
Legacy
|
||||
|
||||
AliasTypeUnknown AliasType = -1
|
||||
)
|
29
vendor/golang.org/x/text/internal/language/compact.go
generated
vendored
29
vendor/golang.org/x/text/internal/language/compact.go
generated
vendored
@ -1,29 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
// CompactCoreInfo is a compact integer with the three core tags encoded.
|
||||
type CompactCoreInfo uint32
|
||||
|
||||
// GetCompactCore generates a uint32 value that is guaranteed to be unique for
|
||||
// different language, region, and script values.
|
||||
func GetCompactCore(t Tag) (cci CompactCoreInfo, ok bool) {
|
||||
if t.LangID > langNoIndexOffset {
|
||||
return 0, false
|
||||
}
|
||||
cci |= CompactCoreInfo(t.LangID) << (8 + 12)
|
||||
cci |= CompactCoreInfo(t.ScriptID) << 12
|
||||
cci |= CompactCoreInfo(t.RegionID)
|
||||
return cci, true
|
||||
}
|
||||
|
||||
// Tag generates a tag from c.
|
||||
func (c CompactCoreInfo) Tag() Tag {
|
||||
return Tag{
|
||||
LangID: Language(c >> 20),
|
||||
RegionID: Region(c & 0x3ff),
|
||||
ScriptID: Script(c>>12) & 0xff,
|
||||
}
|
||||
}
|
61
vendor/golang.org/x/text/internal/language/compact/compact.go
generated
vendored
61
vendor/golang.org/x/text/internal/language/compact/compact.go
generated
vendored
@ -1,61 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package compact defines a compact representation of language tags.
|
||||
//
|
||||
// Common language tags (at least all for which locale information is defined
|
||||
// in CLDR) are assigned a unique index. Each Tag is associated with such an
|
||||
// ID for selecting language-related resources (such as translations) as well
|
||||
// as one for selecting regional defaults (currency, number formatting, etc.)
|
||||
//
|
||||
// It may want to export this functionality at some point, but at this point
|
||||
// this is only available for use within x/text.
|
||||
package compact // import "golang.org/x/text/internal/language/compact"
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/internal/language"
|
||||
)
|
||||
|
||||
// ID is an integer identifying a single tag.
|
||||
type ID uint16
|
||||
|
||||
func getCoreIndex(t language.Tag) (id ID, ok bool) {
|
||||
cci, ok := language.GetCompactCore(t)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
i := sort.Search(len(coreTags), func(i int) bool {
|
||||
return cci <= coreTags[i]
|
||||
})
|
||||
if i == len(coreTags) || coreTags[i] != cci {
|
||||
return 0, false
|
||||
}
|
||||
return ID(i), true
|
||||
}
|
||||
|
||||
// Parent returns the ID of the parent or the root ID if id is already the root.
|
||||
func (id ID) Parent() ID {
|
||||
return parents[id]
|
||||
}
|
||||
|
||||
// Tag converts id to an internal language Tag.
|
||||
func (id ID) Tag() language.Tag {
|
||||
if int(id) >= len(coreTags) {
|
||||
return specialTags[int(id)-len(coreTags)]
|
||||
}
|
||||
return coreTags[id].Tag()
|
||||
}
|
||||
|
||||
var specialTags []language.Tag
|
||||
|
||||
func init() {
|
||||
tags := strings.Split(specialTagsStr, " ")
|
||||
specialTags = make([]language.Tag, len(tags))
|
||||
for i, t := range tags {
|
||||
specialTags[i] = language.MustParse(t)
|
||||
}
|
||||
}
|
260
vendor/golang.org/x/text/internal/language/compact/language.go
generated
vendored
260
vendor/golang.org/x/text/internal/language/compact/language.go
generated
vendored
@ -1,260 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go gen_index.go -output tables.go
|
||||
//go:generate go run gen_parents.go
|
||||
|
||||
package compact
|
||||
|
||||
// TODO: Remove above NOTE after:
|
||||
// - verifying that tables are dropped correctly (most notably matcher tables).
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/internal/language"
|
||||
)
|
||||
|
||||
// Tag represents a BCP 47 language tag. It is used to specify an instance of a
|
||||
// specific language or locale. All language tag values are guaranteed to be
|
||||
// well-formed.
|
||||
type Tag struct {
|
||||
// NOTE: exported tags will become part of the public API.
|
||||
language ID
|
||||
locale ID
|
||||
full fullTag // always a language.Tag for now.
|
||||
}
|
||||
|
||||
const _und = 0
|
||||
|
||||
type fullTag interface {
|
||||
IsRoot() bool
|
||||
Parent() language.Tag
|
||||
}
|
||||
|
||||
// Make a compact Tag from a fully specified internal language Tag.
|
||||
func Make(t language.Tag) (tag Tag) {
|
||||
if region := t.TypeForKey("rg"); len(region) == 6 && region[2:] == "zzzz" {
|
||||
if r, err := language.ParseRegion(region[:2]); err == nil {
|
||||
tFull := t
|
||||
t, _ = t.SetTypeForKey("rg", "")
|
||||
// TODO: should we not consider "va" for the language tag?
|
||||
var exact1, exact2 bool
|
||||
tag.language, exact1 = FromTag(t)
|
||||
t.RegionID = r
|
||||
tag.locale, exact2 = FromTag(t)
|
||||
if !exact1 || !exact2 {
|
||||
tag.full = tFull
|
||||
}
|
||||
return tag
|
||||
}
|
||||
}
|
||||
lang, ok := FromTag(t)
|
||||
tag.language = lang
|
||||
tag.locale = lang
|
||||
if !ok {
|
||||
tag.full = t
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
// Tag returns an internal language Tag version of this tag.
|
||||
func (t Tag) Tag() language.Tag {
|
||||
if t.full != nil {
|
||||
return t.full.(language.Tag)
|
||||
}
|
||||
tag := t.language.Tag()
|
||||
if t.language != t.locale {
|
||||
loc := t.locale.Tag()
|
||||
tag, _ = tag.SetTypeForKey("rg", strings.ToLower(loc.RegionID.String())+"zzzz")
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
// IsCompact reports whether this tag is fully defined in terms of ID.
|
||||
func (t *Tag) IsCompact() bool {
|
||||
return t.full == nil
|
||||
}
|
||||
|
||||
// MayHaveVariants reports whether a tag may have variants. If it returns false
|
||||
// it is guaranteed the tag does not have variants.
|
||||
func (t Tag) MayHaveVariants() bool {
|
||||
return t.full != nil || int(t.language) >= len(coreTags)
|
||||
}
|
||||
|
||||
// MayHaveExtensions reports whether a tag may have extensions. If it returns
|
||||
// false it is guaranteed the tag does not have them.
|
||||
func (t Tag) MayHaveExtensions() bool {
|
||||
return t.full != nil ||
|
||||
int(t.language) >= len(coreTags) ||
|
||||
t.language != t.locale
|
||||
}
|
||||
|
||||
// IsRoot returns true if t is equal to language "und".
|
||||
func (t Tag) IsRoot() bool {
|
||||
if t.full != nil {
|
||||
return t.full.IsRoot()
|
||||
}
|
||||
return t.language == _und
|
||||
}
|
||||
|
||||
// Parent returns the CLDR parent of t. In CLDR, missing fields in data for a
|
||||
// specific language are substituted with fields from the parent language.
|
||||
// The parent for a language may change for newer versions of CLDR.
|
||||
func (t Tag) Parent() Tag {
|
||||
if t.full != nil {
|
||||
return Make(t.full.Parent())
|
||||
}
|
||||
if t.language != t.locale {
|
||||
// Simulate stripping -u-rg-xxxxxx
|
||||
return Tag{language: t.language, locale: t.language}
|
||||
}
|
||||
// TODO: use parent lookup table once cycle from internal package is
|
||||
// removed. Probably by internalizing the table and declaring this fast
|
||||
// enough.
|
||||
// lang := compactID(internal.Parent(uint16(t.language)))
|
||||
lang, _ := FromTag(t.language.Tag().Parent())
|
||||
return Tag{language: lang, locale: lang}
|
||||
}
|
||||
|
||||
// returns token t and the rest of the string.
|
||||
func nextToken(s string) (t, tail string) {
|
||||
p := strings.Index(s[1:], "-")
|
||||
if p == -1 {
|
||||
return s[1:], ""
|
||||
}
|
||||
p++
|
||||
return s[1:p], s[p:]
|
||||
}
|
||||
|
||||
// LanguageID returns an index, where 0 <= index < NumCompactTags, for tags
|
||||
// for which data exists in the text repository.The index will change over time
|
||||
// and should not be stored in persistent storage. If t does not match a compact
|
||||
// index, exact will be false and the compact index will be returned for the
|
||||
// first match after repeatedly taking the Parent of t.
|
||||
func LanguageID(t Tag) (id ID, exact bool) {
|
||||
return t.language, t.full == nil
|
||||
}
|
||||
|
||||
// RegionalID returns the ID for the regional variant of this tag. This index is
|
||||
// used to indicate region-specific overrides, such as default currency, default
|
||||
// calendar and week data, default time cycle, and default measurement system
|
||||
// and unit preferences.
|
||||
//
|
||||
// For instance, the tag en-GB-u-rg-uszzzz specifies British English with US
|
||||
// settings for currency, number formatting, etc. The CompactIndex for this tag
|
||||
// will be that for en-GB, while the RegionalID will be the one corresponding to
|
||||
// en-US.
|
||||
func RegionalID(t Tag) (id ID, exact bool) {
|
||||
return t.locale, t.full == nil
|
||||
}
|
||||
|
||||
// LanguageTag returns t stripped of regional variant indicators.
|
||||
//
|
||||
// At the moment this means it is stripped of a regional and variant subtag "rg"
|
||||
// and "va" in the "u" extension.
|
||||
func (t Tag) LanguageTag() Tag {
|
||||
if t.full == nil {
|
||||
return Tag{language: t.language, locale: t.language}
|
||||
}
|
||||
tt := t.Tag()
|
||||
tt.SetTypeForKey("rg", "")
|
||||
tt.SetTypeForKey("va", "")
|
||||
return Make(tt)
|
||||
}
|
||||
|
||||
// RegionalTag returns the regional variant of the tag.
|
||||
//
|
||||
// At the moment this means that the region is set from the regional subtag
|
||||
// "rg" in the "u" extension.
|
||||
func (t Tag) RegionalTag() Tag {
|
||||
rt := Tag{language: t.locale, locale: t.locale}
|
||||
if t.full == nil {
|
||||
return rt
|
||||
}
|
||||
b := language.Builder{}
|
||||
tag := t.Tag()
|
||||
// tag, _ = tag.SetTypeForKey("rg", "")
|
||||
b.SetTag(t.locale.Tag())
|
||||
if v := tag.Variants(); v != "" {
|
||||
for _, v := range strings.Split(v, "-") {
|
||||
b.AddVariant(v)
|
||||
}
|
||||
}
|
||||
for _, e := range tag.Extensions() {
|
||||
b.AddExt(e)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// FromTag reports closest matching ID for an internal language Tag.
|
||||
func FromTag(t language.Tag) (id ID, exact bool) {
|
||||
// TODO: perhaps give more frequent tags a lower index.
|
||||
// TODO: we could make the indexes stable. This will excluded some
|
||||
// possibilities for optimization, so don't do this quite yet.
|
||||
exact = true
|
||||
|
||||
b, s, r := t.Raw()
|
||||
if t.HasString() {
|
||||
if t.IsPrivateUse() {
|
||||
// We have no entries for user-defined tags.
|
||||
return 0, false
|
||||
}
|
||||
hasExtra := false
|
||||
if t.HasVariants() {
|
||||
if t.HasExtensions() {
|
||||
build := language.Builder{}
|
||||
build.SetTag(language.Tag{LangID: b, ScriptID: s, RegionID: r})
|
||||
build.AddVariant(t.Variants())
|
||||
exact = false
|
||||
t = build.Make()
|
||||
}
|
||||
hasExtra = true
|
||||
} else if _, ok := t.Extension('u'); ok {
|
||||
// TODO: va may mean something else. Consider not considering it.
|
||||
// Strip all but the 'va' entry.
|
||||
old := t
|
||||
variant := t.TypeForKey("va")
|
||||
t = language.Tag{LangID: b, ScriptID: s, RegionID: r}
|
||||
if variant != "" {
|
||||
t, _ = t.SetTypeForKey("va", variant)
|
||||
hasExtra = true
|
||||
}
|
||||
exact = old == t
|
||||
} else {
|
||||
exact = false
|
||||
}
|
||||
if hasExtra {
|
||||
// We have some variants.
|
||||
for i, s := range specialTags {
|
||||
if s == t {
|
||||
return ID(i + len(coreTags)), exact
|
||||
}
|
||||
}
|
||||
exact = false
|
||||
}
|
||||
}
|
||||
if x, ok := getCoreIndex(t); ok {
|
||||
return x, exact
|
||||
}
|
||||
exact = false
|
||||
if r != 0 && s == 0 {
|
||||
// Deal with cases where an extra script is inserted for the region.
|
||||
t, _ := t.Maximize()
|
||||
if x, ok := getCoreIndex(t); ok {
|
||||
return x, exact
|
||||
}
|
||||
}
|
||||
for t = t.Parent(); t != root; t = t.Parent() {
|
||||
// No variants specified: just compare core components.
|
||||
// The key has the form lllssrrr, where l, s, and r are nibbles for
|
||||
// respectively the langID, scriptID, and regionID.
|
||||
if x, ok := getCoreIndex(t); ok {
|
||||
return x, exact
|
||||
}
|
||||
}
|
||||
return 0, exact
|
||||
}
|
||||
|
||||
var root = language.Tag{}
|
120
vendor/golang.org/x/text/internal/language/compact/parents.go
generated
vendored
120
vendor/golang.org/x/text/internal/language/compact/parents.go
generated
vendored
@ -1,120 +0,0 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
package compact
|
||||
|
||||
// parents maps a compact index of a tag to the compact index of the parent of
|
||||
// this tag.
|
||||
var parents = []ID{ // 775 elements
|
||||
// Entry 0 - 3F
|
||||
0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006,
|
||||
0x0000, 0x0008, 0x0000, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a,
|
||||
0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a,
|
||||
0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a,
|
||||
0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x0000,
|
||||
0x0000, 0x0028, 0x0000, 0x002a, 0x0000, 0x002c, 0x0000, 0x0000,
|
||||
0x002f, 0x002e, 0x002e, 0x0000, 0x0033, 0x0000, 0x0035, 0x0000,
|
||||
0x0037, 0x0000, 0x0039, 0x0000, 0x003b, 0x0000, 0x0000, 0x003e,
|
||||
// Entry 40 - 7F
|
||||
0x0000, 0x0040, 0x0040, 0x0000, 0x0043, 0x0043, 0x0000, 0x0046,
|
||||
0x0000, 0x0048, 0x0000, 0x0000, 0x004b, 0x004a, 0x004a, 0x0000,
|
||||
0x004f, 0x004f, 0x004f, 0x004f, 0x0000, 0x0054, 0x0054, 0x0000,
|
||||
0x0057, 0x0000, 0x0059, 0x0000, 0x005b, 0x0000, 0x005d, 0x005d,
|
||||
0x0000, 0x0060, 0x0000, 0x0062, 0x0000, 0x0064, 0x0000, 0x0066,
|
||||
0x0066, 0x0000, 0x0069, 0x0000, 0x006b, 0x006b, 0x006b, 0x006b,
|
||||
0x006b, 0x006b, 0x006b, 0x0000, 0x0073, 0x0000, 0x0075, 0x0000,
|
||||
0x0077, 0x0000, 0x0000, 0x007a, 0x0000, 0x007c, 0x0000, 0x007e,
|
||||
// Entry 80 - BF
|
||||
0x0000, 0x0080, 0x0080, 0x0000, 0x0083, 0x0083, 0x0000, 0x0086,
|
||||
0x0087, 0x0087, 0x0087, 0x0086, 0x0088, 0x0087, 0x0087, 0x0087,
|
||||
0x0086, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0088,
|
||||
0x0087, 0x0087, 0x0087, 0x0087, 0x0088, 0x0087, 0x0088, 0x0087,
|
||||
0x0087, 0x0088, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087,
|
||||
0x0087, 0x0087, 0x0087, 0x0086, 0x0087, 0x0087, 0x0087, 0x0087,
|
||||
0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087,
|
||||
0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0086, 0x0087, 0x0086,
|
||||
// Entry C0 - FF
|
||||
0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087,
|
||||
0x0088, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087,
|
||||
0x0086, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0088, 0x0087,
|
||||
0x0087, 0x0088, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087,
|
||||
0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0086, 0x0086, 0x0087,
|
||||
0x0087, 0x0086, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0000,
|
||||
0x00ef, 0x0000, 0x00f1, 0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x00f2,
|
||||
0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x00f1, 0x00f2, 0x00f1, 0x00f1,
|
||||
// Entry 100 - 13F
|
||||
0x00f2, 0x00f2, 0x00f1, 0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x00f1,
|
||||
0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x0000, 0x010e,
|
||||
0x0000, 0x0110, 0x0000, 0x0112, 0x0000, 0x0114, 0x0114, 0x0000,
|
||||
0x0117, 0x0117, 0x0117, 0x0117, 0x0000, 0x011c, 0x0000, 0x011e,
|
||||
0x0000, 0x0120, 0x0120, 0x0000, 0x0123, 0x0123, 0x0123, 0x0123,
|
||||
0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123,
|
||||
0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123,
|
||||
0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123,
|
||||
// Entry 140 - 17F
|
||||
0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123,
|
||||
0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123, 0x0123,
|
||||
0x0123, 0x0123, 0x0000, 0x0152, 0x0000, 0x0154, 0x0000, 0x0156,
|
||||
0x0000, 0x0158, 0x0000, 0x015a, 0x0000, 0x015c, 0x015c, 0x015c,
|
||||
0x0000, 0x0160, 0x0000, 0x0000, 0x0163, 0x0000, 0x0165, 0x0000,
|
||||
0x0167, 0x0167, 0x0167, 0x0000, 0x016b, 0x0000, 0x016d, 0x0000,
|
||||
0x016f, 0x0000, 0x0171, 0x0171, 0x0000, 0x0174, 0x0000, 0x0176,
|
||||
0x0000, 0x0178, 0x0000, 0x017a, 0x0000, 0x017c, 0x0000, 0x017e,
|
||||
// Entry 180 - 1BF
|
||||
0x0000, 0x0000, 0x0000, 0x0182, 0x0000, 0x0184, 0x0184, 0x0184,
|
||||
0x0184, 0x0000, 0x0000, 0x0000, 0x018b, 0x0000, 0x0000, 0x018e,
|
||||
0x0000, 0x0000, 0x0191, 0x0000, 0x0000, 0x0000, 0x0195, 0x0000,
|
||||
0x0197, 0x0000, 0x0000, 0x019a, 0x0000, 0x0000, 0x019d, 0x0000,
|
||||
0x019f, 0x0000, 0x01a1, 0x0000, 0x01a3, 0x0000, 0x01a5, 0x0000,
|
||||
0x01a7, 0x0000, 0x01a9, 0x0000, 0x01ab, 0x0000, 0x01ad, 0x0000,
|
||||
0x01af, 0x0000, 0x01b1, 0x01b1, 0x0000, 0x01b4, 0x0000, 0x01b6,
|
||||
0x0000, 0x01b8, 0x0000, 0x01ba, 0x0000, 0x01bc, 0x0000, 0x0000,
|
||||
// Entry 1C0 - 1FF
|
||||
0x01bf, 0x0000, 0x01c1, 0x0000, 0x01c3, 0x0000, 0x01c5, 0x0000,
|
||||
0x01c7, 0x0000, 0x01c9, 0x0000, 0x01cb, 0x01cb, 0x01cb, 0x01cb,
|
||||
0x0000, 0x01d0, 0x0000, 0x01d2, 0x01d2, 0x0000, 0x01d5, 0x0000,
|
||||
0x01d7, 0x0000, 0x01d9, 0x0000, 0x01db, 0x0000, 0x01dd, 0x0000,
|
||||
0x01df, 0x01df, 0x0000, 0x01e2, 0x0000, 0x01e4, 0x0000, 0x01e6,
|
||||
0x0000, 0x01e8, 0x0000, 0x01ea, 0x0000, 0x01ec, 0x0000, 0x01ee,
|
||||
0x0000, 0x01f0, 0x0000, 0x0000, 0x01f3, 0x0000, 0x01f5, 0x01f5,
|
||||
0x01f5, 0x0000, 0x01f9, 0x0000, 0x01fb, 0x0000, 0x01fd, 0x0000,
|
||||
// Entry 200 - 23F
|
||||
0x01ff, 0x0000, 0x0000, 0x0202, 0x0000, 0x0204, 0x0204, 0x0000,
|
||||
0x0207, 0x0000, 0x0209, 0x0209, 0x0000, 0x020c, 0x020c, 0x0000,
|
||||
0x020f, 0x020f, 0x020f, 0x020f, 0x020f, 0x020f, 0x020f, 0x0000,
|
||||
0x0217, 0x0000, 0x0219, 0x0000, 0x021b, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0221, 0x0000, 0x0000, 0x0224, 0x0000, 0x0226,
|
||||
0x0226, 0x0000, 0x0229, 0x0000, 0x022b, 0x022b, 0x0000, 0x0000,
|
||||
0x022f, 0x022e, 0x022e, 0x0000, 0x0000, 0x0234, 0x0000, 0x0236,
|
||||
0x0000, 0x0238, 0x0000, 0x0244, 0x023a, 0x0244, 0x0244, 0x0244,
|
||||
// Entry 240 - 27F
|
||||
0x0244, 0x0244, 0x0244, 0x0244, 0x023a, 0x0244, 0x0244, 0x0000,
|
||||
0x0247, 0x0247, 0x0247, 0x0000, 0x024b, 0x0000, 0x024d, 0x0000,
|
||||
0x024f, 0x024f, 0x0000, 0x0252, 0x0000, 0x0254, 0x0254, 0x0254,
|
||||
0x0254, 0x0254, 0x0254, 0x0000, 0x025b, 0x0000, 0x025d, 0x0000,
|
||||
0x025f, 0x0000, 0x0261, 0x0000, 0x0263, 0x0000, 0x0265, 0x0000,
|
||||
0x0000, 0x0268, 0x0268, 0x0268, 0x0000, 0x026c, 0x0000, 0x026e,
|
||||
0x0000, 0x0270, 0x0000, 0x0000, 0x0000, 0x0274, 0x0273, 0x0273,
|
||||
0x0000, 0x0278, 0x0000, 0x027a, 0x0000, 0x027c, 0x0000, 0x0000,
|
||||
// Entry 280 - 2BF
|
||||
0x0000, 0x0000, 0x0281, 0x0000, 0x0000, 0x0284, 0x0000, 0x0286,
|
||||
0x0286, 0x0286, 0x0286, 0x0000, 0x028b, 0x028b, 0x028b, 0x0000,
|
||||
0x028f, 0x028f, 0x028f, 0x028f, 0x028f, 0x0000, 0x0295, 0x0295,
|
||||
0x0295, 0x0295, 0x0000, 0x0000, 0x0000, 0x0000, 0x029d, 0x029d,
|
||||
0x029d, 0x0000, 0x02a1, 0x02a1, 0x02a1, 0x02a1, 0x0000, 0x0000,
|
||||
0x02a7, 0x02a7, 0x02a7, 0x02a7, 0x0000, 0x02ac, 0x0000, 0x02ae,
|
||||
0x02ae, 0x0000, 0x02b1, 0x0000, 0x02b3, 0x0000, 0x02b5, 0x02b5,
|
||||
0x0000, 0x0000, 0x02b9, 0x0000, 0x0000, 0x0000, 0x02bd, 0x0000,
|
||||
// Entry 2C0 - 2FF
|
||||
0x02bf, 0x02bf, 0x0000, 0x0000, 0x02c3, 0x0000, 0x02c5, 0x0000,
|
||||
0x02c7, 0x0000, 0x02c9, 0x0000, 0x02cb, 0x0000, 0x02cd, 0x02cd,
|
||||
0x0000, 0x0000, 0x02d1, 0x0000, 0x02d3, 0x02d0, 0x02d0, 0x0000,
|
||||
0x0000, 0x02d8, 0x02d7, 0x02d7, 0x0000, 0x0000, 0x02dd, 0x0000,
|
||||
0x02df, 0x0000, 0x02e1, 0x0000, 0x0000, 0x02e4, 0x0000, 0x02e6,
|
||||
0x0000, 0x0000, 0x02e9, 0x0000, 0x02eb, 0x0000, 0x02ed, 0x0000,
|
||||
0x02ef, 0x02ef, 0x0000, 0x0000, 0x02f3, 0x02f2, 0x02f2, 0x0000,
|
||||
0x02f7, 0x0000, 0x02f9, 0x02f9, 0x02f9, 0x02f9, 0x02f9, 0x0000,
|
||||
// Entry 300 - 33F
|
||||
0x02ff, 0x0300, 0x02ff, 0x0000, 0x0303, 0x0051, 0x00e6,
|
||||
} // Size: 1574 bytes
|
||||
|
||||
// Total table size 1574 bytes (1KiB); checksum: 895AAF0B
|
1015
vendor/golang.org/x/text/internal/language/compact/tables.go
generated
vendored
1015
vendor/golang.org/x/text/internal/language/compact/tables.go
generated
vendored
File diff suppressed because it is too large
Load Diff
91
vendor/golang.org/x/text/internal/language/compact/tags.go
generated
vendored
91
vendor/golang.org/x/text/internal/language/compact/tags.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package compact
|
||||
|
||||
var (
|
||||
und = Tag{}
|
||||
|
||||
Und Tag = Tag{}
|
||||
|
||||
Afrikaans Tag = Tag{language: afIndex, locale: afIndex}
|
||||
Amharic Tag = Tag{language: amIndex, locale: amIndex}
|
||||
Arabic Tag = Tag{language: arIndex, locale: arIndex}
|
||||
ModernStandardArabic Tag = Tag{language: ar001Index, locale: ar001Index}
|
||||
Azerbaijani Tag = Tag{language: azIndex, locale: azIndex}
|
||||
Bulgarian Tag = Tag{language: bgIndex, locale: bgIndex}
|
||||
Bengali Tag = Tag{language: bnIndex, locale: bnIndex}
|
||||
Catalan Tag = Tag{language: caIndex, locale: caIndex}
|
||||
Czech Tag = Tag{language: csIndex, locale: csIndex}
|
||||
Danish Tag = Tag{language: daIndex, locale: daIndex}
|
||||
German Tag = Tag{language: deIndex, locale: deIndex}
|
||||
Greek Tag = Tag{language: elIndex, locale: elIndex}
|
||||
English Tag = Tag{language: enIndex, locale: enIndex}
|
||||
AmericanEnglish Tag = Tag{language: enUSIndex, locale: enUSIndex}
|
||||
BritishEnglish Tag = Tag{language: enGBIndex, locale: enGBIndex}
|
||||
Spanish Tag = Tag{language: esIndex, locale: esIndex}
|
||||
EuropeanSpanish Tag = Tag{language: esESIndex, locale: esESIndex}
|
||||
LatinAmericanSpanish Tag = Tag{language: es419Index, locale: es419Index}
|
||||
Estonian Tag = Tag{language: etIndex, locale: etIndex}
|
||||
Persian Tag = Tag{language: faIndex, locale: faIndex}
|
||||
Finnish Tag = Tag{language: fiIndex, locale: fiIndex}
|
||||
Filipino Tag = Tag{language: filIndex, locale: filIndex}
|
||||
French Tag = Tag{language: frIndex, locale: frIndex}
|
||||
CanadianFrench Tag = Tag{language: frCAIndex, locale: frCAIndex}
|
||||
Gujarati Tag = Tag{language: guIndex, locale: guIndex}
|
||||
Hebrew Tag = Tag{language: heIndex, locale: heIndex}
|
||||
Hindi Tag = Tag{language: hiIndex, locale: hiIndex}
|
||||
Croatian Tag = Tag{language: hrIndex, locale: hrIndex}
|
||||
Hungarian Tag = Tag{language: huIndex, locale: huIndex}
|
||||
Armenian Tag = Tag{language: hyIndex, locale: hyIndex}
|
||||
Indonesian Tag = Tag{language: idIndex, locale: idIndex}
|
||||
Icelandic Tag = Tag{language: isIndex, locale: isIndex}
|
||||
Italian Tag = Tag{language: itIndex, locale: itIndex}
|
||||
Japanese Tag = Tag{language: jaIndex, locale: jaIndex}
|
||||
Georgian Tag = Tag{language: kaIndex, locale: kaIndex}
|
||||
Kazakh Tag = Tag{language: kkIndex, locale: kkIndex}
|
||||
Khmer Tag = Tag{language: kmIndex, locale: kmIndex}
|
||||
Kannada Tag = Tag{language: knIndex, locale: knIndex}
|
||||
Korean Tag = Tag{language: koIndex, locale: koIndex}
|
||||
Kirghiz Tag = Tag{language: kyIndex, locale: kyIndex}
|
||||
Lao Tag = Tag{language: loIndex, locale: loIndex}
|
||||
Lithuanian Tag = Tag{language: ltIndex, locale: ltIndex}
|
||||
Latvian Tag = Tag{language: lvIndex, locale: lvIndex}
|
||||
Macedonian Tag = Tag{language: mkIndex, locale: mkIndex}
|
||||
Malayalam Tag = Tag{language: mlIndex, locale: mlIndex}
|
||||
Mongolian Tag = Tag{language: mnIndex, locale: mnIndex}
|
||||
Marathi Tag = Tag{language: mrIndex, locale: mrIndex}
|
||||
Malay Tag = Tag{language: msIndex, locale: msIndex}
|
||||
Burmese Tag = Tag{language: myIndex, locale: myIndex}
|
||||
Nepali Tag = Tag{language: neIndex, locale: neIndex}
|
||||
Dutch Tag = Tag{language: nlIndex, locale: nlIndex}
|
||||
Norwegian Tag = Tag{language: noIndex, locale: noIndex}
|
||||
Punjabi Tag = Tag{language: paIndex, locale: paIndex}
|
||||
Polish Tag = Tag{language: plIndex, locale: plIndex}
|
||||
Portuguese Tag = Tag{language: ptIndex, locale: ptIndex}
|
||||
BrazilianPortuguese Tag = Tag{language: ptBRIndex, locale: ptBRIndex}
|
||||
EuropeanPortuguese Tag = Tag{language: ptPTIndex, locale: ptPTIndex}
|
||||
Romanian Tag = Tag{language: roIndex, locale: roIndex}
|
||||
Russian Tag = Tag{language: ruIndex, locale: ruIndex}
|
||||
Sinhala Tag = Tag{language: siIndex, locale: siIndex}
|
||||
Slovak Tag = Tag{language: skIndex, locale: skIndex}
|
||||
Slovenian Tag = Tag{language: slIndex, locale: slIndex}
|
||||
Albanian Tag = Tag{language: sqIndex, locale: sqIndex}
|
||||
Serbian Tag = Tag{language: srIndex, locale: srIndex}
|
||||
SerbianLatin Tag = Tag{language: srLatnIndex, locale: srLatnIndex}
|
||||
Swedish Tag = Tag{language: svIndex, locale: svIndex}
|
||||
Swahili Tag = Tag{language: swIndex, locale: swIndex}
|
||||
Tamil Tag = Tag{language: taIndex, locale: taIndex}
|
||||
Telugu Tag = Tag{language: teIndex, locale: teIndex}
|
||||
Thai Tag = Tag{language: thIndex, locale: thIndex}
|
||||
Turkish Tag = Tag{language: trIndex, locale: trIndex}
|
||||
Ukrainian Tag = Tag{language: ukIndex, locale: ukIndex}
|
||||
Urdu Tag = Tag{language: urIndex, locale: urIndex}
|
||||
Uzbek Tag = Tag{language: uzIndex, locale: uzIndex}
|
||||
Vietnamese Tag = Tag{language: viIndex, locale: viIndex}
|
||||
Chinese Tag = Tag{language: zhIndex, locale: zhIndex}
|
||||
SimplifiedChinese Tag = Tag{language: zhHansIndex, locale: zhHansIndex}
|
||||
TraditionalChinese Tag = Tag{language: zhHantIndex, locale: zhHantIndex}
|
||||
Zulu Tag = Tag{language: zuIndex, locale: zuIndex}
|
||||
)
|
167
vendor/golang.org/x/text/internal/language/compose.go
generated
vendored
167
vendor/golang.org/x/text/internal/language/compose.go
generated
vendored
@ -1,167 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Builder allows constructing a Tag from individual components.
|
||||
// Its main user is Compose in the top-level language package.
|
||||
type Builder struct {
|
||||
Tag Tag
|
||||
|
||||
private string // the x extension
|
||||
variants []string
|
||||
extensions []string
|
||||
}
|
||||
|
||||
// Make returns a new Tag from the current settings.
|
||||
func (b *Builder) Make() Tag {
|
||||
t := b.Tag
|
||||
|
||||
if len(b.extensions) > 0 || len(b.variants) > 0 {
|
||||
sort.Sort(sortVariants(b.variants))
|
||||
sort.Strings(b.extensions)
|
||||
|
||||
if b.private != "" {
|
||||
b.extensions = append(b.extensions, b.private)
|
||||
}
|
||||
n := maxCoreSize + tokenLen(b.variants...) + tokenLen(b.extensions...)
|
||||
buf := make([]byte, n)
|
||||
p := t.genCoreBytes(buf)
|
||||
t.pVariant = byte(p)
|
||||
p += appendTokens(buf[p:], b.variants...)
|
||||
t.pExt = uint16(p)
|
||||
p += appendTokens(buf[p:], b.extensions...)
|
||||
t.str = string(buf[:p])
|
||||
// We may not always need to remake the string, but when or when not
|
||||
// to do so is rather tricky.
|
||||
scan := makeScanner(buf[:p])
|
||||
t, _ = parse(&scan, "")
|
||||
return t
|
||||
|
||||
} else if b.private != "" {
|
||||
t.str = b.private
|
||||
t.RemakeString()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// SetTag copies all the settings from a given Tag. Any previously set values
|
||||
// are discarded.
|
||||
func (b *Builder) SetTag(t Tag) {
|
||||
b.Tag.LangID = t.LangID
|
||||
b.Tag.RegionID = t.RegionID
|
||||
b.Tag.ScriptID = t.ScriptID
|
||||
// TODO: optimize
|
||||
b.variants = b.variants[:0]
|
||||
if variants := t.Variants(); variants != "" {
|
||||
for _, vr := range strings.Split(variants[1:], "-") {
|
||||
b.variants = append(b.variants, vr)
|
||||
}
|
||||
}
|
||||
b.extensions, b.private = b.extensions[:0], ""
|
||||
for _, e := range t.Extensions() {
|
||||
b.AddExt(e)
|
||||
}
|
||||
}
|
||||
|
||||
// AddExt adds extension e to the tag. e must be a valid extension as returned
|
||||
// by Tag.Extension. If the extension already exists, it will be discarded,
|
||||
// except for a -u extension, where non-existing key-type pairs will added.
|
||||
func (b *Builder) AddExt(e string) {
|
||||
if e[0] == 'x' {
|
||||
if b.private == "" {
|
||||
b.private = e
|
||||
}
|
||||
return
|
||||
}
|
||||
for i, s := range b.extensions {
|
||||
if s[0] == e[0] {
|
||||
if e[0] == 'u' {
|
||||
b.extensions[i] += e[1:]
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
b.extensions = append(b.extensions, e)
|
||||
}
|
||||
|
||||
// SetExt sets the extension e to the tag. e must be a valid extension as
|
||||
// returned by Tag.Extension. If the extension already exists, it will be
|
||||
// overwritten, except for a -u extension, where the individual key-type pairs
|
||||
// will be set.
|
||||
func (b *Builder) SetExt(e string) {
|
||||
if e[0] == 'x' {
|
||||
b.private = e
|
||||
return
|
||||
}
|
||||
for i, s := range b.extensions {
|
||||
if s[0] == e[0] {
|
||||
if e[0] == 'u' {
|
||||
b.extensions[i] = e + s[1:]
|
||||
} else {
|
||||
b.extensions[i] = e
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
b.extensions = append(b.extensions, e)
|
||||
}
|
||||
|
||||
// AddVariant adds any number of variants.
|
||||
func (b *Builder) AddVariant(v ...string) {
|
||||
for _, v := range v {
|
||||
if v != "" {
|
||||
b.variants = append(b.variants, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ClearVariants removes any variants previously added, including those
|
||||
// copied from a Tag in SetTag.
|
||||
func (b *Builder) ClearVariants() {
|
||||
b.variants = b.variants[:0]
|
||||
}
|
||||
|
||||
// ClearExtensions removes any extensions previously added, including those
|
||||
// copied from a Tag in SetTag.
|
||||
func (b *Builder) ClearExtensions() {
|
||||
b.private = ""
|
||||
b.extensions = b.extensions[:0]
|
||||
}
|
||||
|
||||
func tokenLen(token ...string) (n int) {
|
||||
for _, t := range token {
|
||||
n += len(t) + 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func appendTokens(b []byte, token ...string) int {
|
||||
p := 0
|
||||
for _, t := range token {
|
||||
b[p] = '-'
|
||||
copy(b[p+1:], t)
|
||||
p += 1 + len(t)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
type sortVariants []string
|
||||
|
||||
func (s sortVariants) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s sortVariants) Swap(i, j int) {
|
||||
s[j], s[i] = s[i], s[j]
|
||||
}
|
||||
|
||||
func (s sortVariants) Less(i, j int) bool {
|
||||
return variantIndex[s[i]] < variantIndex[s[j]]
|
||||
}
|
28
vendor/golang.org/x/text/internal/language/coverage.go
generated
vendored
28
vendor/golang.org/x/text/internal/language/coverage.go
generated
vendored
@ -1,28 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
// BaseLanguages returns the list of all supported base languages. It generates
|
||||
// the list by traversing the internal structures.
|
||||
func BaseLanguages() []Language {
|
||||
base := make([]Language, 0, NumLanguages)
|
||||
for i := 0; i < langNoIndexOffset; i++ {
|
||||
// We included "und" already for the value 0.
|
||||
if i != nonCanonicalUnd {
|
||||
base = append(base, Language(i))
|
||||
}
|
||||
}
|
||||
i := langNoIndexOffset
|
||||
for _, v := range langNoIndex {
|
||||
for k := 0; k < 8; k++ {
|
||||
if v&1 == 1 {
|
||||
base = append(base, Language(i))
|
||||
}
|
||||
v >>= 1
|
||||
i++
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
596
vendor/golang.org/x/text/internal/language/language.go
generated
vendored
596
vendor/golang.org/x/text/internal/language/language.go
generated
vendored
@ -1,596 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go gen_common.go -output tables.go
|
||||
|
||||
package language // import "golang.org/x/text/internal/language"
|
||||
|
||||
// TODO: Remove above NOTE after:
|
||||
// - verifying that tables are dropped correctly (most notably matcher tables).
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxCoreSize is the maximum size of a BCP 47 tag without variants and
|
||||
// extensions. Equals max lang (3) + script (4) + max reg (3) + 2 dashes.
|
||||
maxCoreSize = 12
|
||||
|
||||
// max99thPercentileSize is a somewhat arbitrary buffer size that presumably
|
||||
// is large enough to hold at least 99% of the BCP 47 tags.
|
||||
max99thPercentileSize = 32
|
||||
|
||||
// maxSimpleUExtensionSize is the maximum size of a -u extension with one
|
||||
// key-type pair. Equals len("-u-") + key (2) + dash + max value (8).
|
||||
maxSimpleUExtensionSize = 14
|
||||
)
|
||||
|
||||
// Tag represents a BCP 47 language tag. It is used to specify an instance of a
|
||||
// specific language or locale. All language tag values are guaranteed to be
|
||||
// well-formed. The zero value of Tag is Und.
|
||||
type Tag struct {
|
||||
// TODO: the following fields have the form TagTypeID. This name is chosen
|
||||
// to allow refactoring the public package without conflicting with its
|
||||
// Base, Script, and Region methods. Once the transition is fully completed
|
||||
// the ID can be stripped from the name.
|
||||
|
||||
LangID Language
|
||||
RegionID Region
|
||||
// TODO: we will soon run out of positions for ScriptID. Idea: instead of
|
||||
// storing lang, region, and ScriptID codes, store only the compact index and
|
||||
// have a lookup table from this code to its expansion. This greatly speeds
|
||||
// up table lookup, speed up common variant cases.
|
||||
// This will also immediately free up 3 extra bytes. Also, the pVariant
|
||||
// field can now be moved to the lookup table, as the compact index uniquely
|
||||
// determines the offset of a possible variant.
|
||||
ScriptID Script
|
||||
pVariant byte // offset in str, includes preceding '-'
|
||||
pExt uint16 // offset of first extension, includes preceding '-'
|
||||
|
||||
// str is the string representation of the Tag. It will only be used if the
|
||||
// tag has variants or extensions.
|
||||
str string
|
||||
}
|
||||
|
||||
// Make is a convenience wrapper for Parse that omits the error.
|
||||
// In case of an error, a sensible default is returned.
|
||||
func Make(s string) Tag {
|
||||
t, _ := Parse(s)
|
||||
return t
|
||||
}
|
||||
|
||||
// Raw returns the raw base language, script and region, without making an
|
||||
// attempt to infer their values.
|
||||
// TODO: consider removing
|
||||
func (t Tag) Raw() (b Language, s Script, r Region) {
|
||||
return t.LangID, t.ScriptID, t.RegionID
|
||||
}
|
||||
|
||||
// equalTags compares language, script and region subtags only.
|
||||
func (t Tag) equalTags(a Tag) bool {
|
||||
return t.LangID == a.LangID && t.ScriptID == a.ScriptID && t.RegionID == a.RegionID
|
||||
}
|
||||
|
||||
// IsRoot returns true if t is equal to language "und".
|
||||
func (t Tag) IsRoot() bool {
|
||||
if int(t.pVariant) < len(t.str) {
|
||||
return false
|
||||
}
|
||||
return t.equalTags(Und)
|
||||
}
|
||||
|
||||
// IsPrivateUse reports whether the Tag consists solely of an IsPrivateUse use
|
||||
// tag.
|
||||
func (t Tag) IsPrivateUse() bool {
|
||||
return t.str != "" && t.pVariant == 0
|
||||
}
|
||||
|
||||
// RemakeString is used to update t.str in case lang, script or region changed.
|
||||
// It is assumed that pExt and pVariant still point to the start of the
|
||||
// respective parts.
|
||||
func (t *Tag) RemakeString() {
|
||||
if t.str == "" {
|
||||
return
|
||||
}
|
||||
extra := t.str[t.pVariant:]
|
||||
if t.pVariant > 0 {
|
||||
extra = extra[1:]
|
||||
}
|
||||
if t.equalTags(Und) && strings.HasPrefix(extra, "x-") {
|
||||
t.str = extra
|
||||
t.pVariant = 0
|
||||
t.pExt = 0
|
||||
return
|
||||
}
|
||||
var buf [max99thPercentileSize]byte // avoid extra memory allocation in most cases.
|
||||
b := buf[:t.genCoreBytes(buf[:])]
|
||||
if extra != "" {
|
||||
diff := len(b) - int(t.pVariant)
|
||||
b = append(b, '-')
|
||||
b = append(b, extra...)
|
||||
t.pVariant = uint8(int(t.pVariant) + diff)
|
||||
t.pExt = uint16(int(t.pExt) + diff)
|
||||
} else {
|
||||
t.pVariant = uint8(len(b))
|
||||
t.pExt = uint16(len(b))
|
||||
}
|
||||
t.str = string(b)
|
||||
}
|
||||
|
||||
// genCoreBytes writes a string for the base languages, script and region tags
|
||||
// to the given buffer and returns the number of bytes written. It will never
|
||||
// write more than maxCoreSize bytes.
|
||||
func (t *Tag) genCoreBytes(buf []byte) int {
|
||||
n := t.LangID.StringToBuf(buf[:])
|
||||
if t.ScriptID != 0 {
|
||||
n += copy(buf[n:], "-")
|
||||
n += copy(buf[n:], t.ScriptID.String())
|
||||
}
|
||||
if t.RegionID != 0 {
|
||||
n += copy(buf[n:], "-")
|
||||
n += copy(buf[n:], t.RegionID.String())
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// String returns the canonical string representation of the language tag.
|
||||
func (t Tag) String() string {
|
||||
if t.str != "" {
|
||||
return t.str
|
||||
}
|
||||
if t.ScriptID == 0 && t.RegionID == 0 {
|
||||
return t.LangID.String()
|
||||
}
|
||||
buf := [maxCoreSize]byte{}
|
||||
return string(buf[:t.genCoreBytes(buf[:])])
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (t Tag) MarshalText() (text []byte, err error) {
|
||||
if t.str != "" {
|
||||
text = append(text, t.str...)
|
||||
} else if t.ScriptID == 0 && t.RegionID == 0 {
|
||||
text = append(text, t.LangID.String()...)
|
||||
} else {
|
||||
buf := [maxCoreSize]byte{}
|
||||
text = buf[:t.genCoreBytes(buf[:])]
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (t *Tag) UnmarshalText(text []byte) error {
|
||||
tag, err := Parse(string(text))
|
||||
*t = tag
|
||||
return err
|
||||
}
|
||||
|
||||
// Variants returns the part of the tag holding all variants or the empty string
|
||||
// if there are no variants defined.
|
||||
func (t Tag) Variants() string {
|
||||
if t.pVariant == 0 {
|
||||
return ""
|
||||
}
|
||||
return t.str[t.pVariant:t.pExt]
|
||||
}
|
||||
|
||||
// VariantOrPrivateUseTags returns variants or private use tags.
|
||||
func (t Tag) VariantOrPrivateUseTags() string {
|
||||
if t.pExt > 0 {
|
||||
return t.str[t.pVariant:t.pExt]
|
||||
}
|
||||
return t.str[t.pVariant:]
|
||||
}
|
||||
|
||||
// HasString reports whether this tag defines more than just the raw
|
||||
// components.
|
||||
func (t Tag) HasString() bool {
|
||||
return t.str != ""
|
||||
}
|
||||
|
||||
// Parent returns the CLDR parent of t. In CLDR, missing fields in data for a
|
||||
// specific language are substituted with fields from the parent language.
|
||||
// The parent for a language may change for newer versions of CLDR.
|
||||
func (t Tag) Parent() Tag {
|
||||
if t.str != "" {
|
||||
// Strip the variants and extensions.
|
||||
b, s, r := t.Raw()
|
||||
t = Tag{LangID: b, ScriptID: s, RegionID: r}
|
||||
if t.RegionID == 0 && t.ScriptID != 0 && t.LangID != 0 {
|
||||
base, _ := addTags(Tag{LangID: t.LangID})
|
||||
if base.ScriptID == t.ScriptID {
|
||||
return Tag{LangID: t.LangID}
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
if t.LangID != 0 {
|
||||
if t.RegionID != 0 {
|
||||
maxScript := t.ScriptID
|
||||
if maxScript == 0 {
|
||||
max, _ := addTags(t)
|
||||
maxScript = max.ScriptID
|
||||
}
|
||||
|
||||
for i := range parents {
|
||||
if Language(parents[i].lang) == t.LangID && Script(parents[i].maxScript) == maxScript {
|
||||
for _, r := range parents[i].fromRegion {
|
||||
if Region(r) == t.RegionID {
|
||||
return Tag{
|
||||
LangID: t.LangID,
|
||||
ScriptID: Script(parents[i].script),
|
||||
RegionID: Region(parents[i].toRegion),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip the script if it is the default one.
|
||||
base, _ := addTags(Tag{LangID: t.LangID})
|
||||
if base.ScriptID != maxScript {
|
||||
return Tag{LangID: t.LangID, ScriptID: maxScript}
|
||||
}
|
||||
return Tag{LangID: t.LangID}
|
||||
} else if t.ScriptID != 0 {
|
||||
// The parent for an base-script pair with a non-default script is
|
||||
// "und" instead of the base language.
|
||||
base, _ := addTags(Tag{LangID: t.LangID})
|
||||
if base.ScriptID != t.ScriptID {
|
||||
return Und
|
||||
}
|
||||
return Tag{LangID: t.LangID}
|
||||
}
|
||||
}
|
||||
return Und
|
||||
}
|
||||
|
||||
// ParseExtension parses s as an extension and returns it on success.
|
||||
func ParseExtension(s string) (ext string, err error) {
|
||||
scan := makeScannerString(s)
|
||||
var end int
|
||||
if n := len(scan.token); n != 1 {
|
||||
return "", ErrSyntax
|
||||
}
|
||||
scan.toLower(0, len(scan.b))
|
||||
end = parseExtension(&scan)
|
||||
if end != len(s) {
|
||||
return "", ErrSyntax
|
||||
}
|
||||
return string(scan.b), nil
|
||||
}
|
||||
|
||||
// HasVariants reports whether t has variants.
|
||||
func (t Tag) HasVariants() bool {
|
||||
return uint16(t.pVariant) < t.pExt
|
||||
}
|
||||
|
||||
// HasExtensions reports whether t has extensions.
|
||||
func (t Tag) HasExtensions() bool {
|
||||
return int(t.pExt) < len(t.str)
|
||||
}
|
||||
|
||||
// Extension returns the extension of type x for tag t. It will return
|
||||
// false for ok if t does not have the requested extension. The returned
|
||||
// extension will be invalid in this case.
|
||||
func (t Tag) Extension(x byte) (ext string, ok bool) {
|
||||
for i := int(t.pExt); i < len(t.str)-1; {
|
||||
var ext string
|
||||
i, ext = getExtension(t.str, i)
|
||||
if ext[0] == x {
|
||||
return ext, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Extensions returns all extensions of t.
|
||||
func (t Tag) Extensions() []string {
|
||||
e := []string{}
|
||||
for i := int(t.pExt); i < len(t.str)-1; {
|
||||
var ext string
|
||||
i, ext = getExtension(t.str, i)
|
||||
e = append(e, ext)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// TypeForKey returns the type associated with the given key, where key and type
|
||||
// are of the allowed values defined for the Unicode locale extension ('u') in
|
||||
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
|
||||
// TypeForKey will traverse the inheritance chain to get the correct value.
|
||||
func (t Tag) TypeForKey(key string) string {
|
||||
if start, end, _ := t.findTypeForKey(key); end != start {
|
||||
return t.str[start:end]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var (
|
||||
errPrivateUse = errors.New("cannot set a key on a private use tag")
|
||||
errInvalidArguments = errors.New("invalid key or type")
|
||||
)
|
||||
|
||||
// SetTypeForKey returns a new Tag with the key set to type, where key and type
|
||||
// are of the allowed values defined for the Unicode locale extension ('u') in
|
||||
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
|
||||
// An empty value removes an existing pair with the same key.
|
||||
func (t Tag) SetTypeForKey(key, value string) (Tag, error) {
|
||||
if t.IsPrivateUse() {
|
||||
return t, errPrivateUse
|
||||
}
|
||||
if len(key) != 2 {
|
||||
return t, errInvalidArguments
|
||||
}
|
||||
|
||||
// Remove the setting if value is "".
|
||||
if value == "" {
|
||||
start, end, _ := t.findTypeForKey(key)
|
||||
if start != end {
|
||||
// Remove key tag and leading '-'.
|
||||
start -= 4
|
||||
|
||||
// Remove a possible empty extension.
|
||||
if (end == len(t.str) || t.str[end+2] == '-') && t.str[start-2] == '-' {
|
||||
start -= 2
|
||||
}
|
||||
if start == int(t.pVariant) && end == len(t.str) {
|
||||
t.str = ""
|
||||
t.pVariant, t.pExt = 0, 0
|
||||
} else {
|
||||
t.str = fmt.Sprintf("%s%s", t.str[:start], t.str[end:])
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
if len(value) < 3 || len(value) > 8 {
|
||||
return t, errInvalidArguments
|
||||
}
|
||||
|
||||
var (
|
||||
buf [maxCoreSize + maxSimpleUExtensionSize]byte
|
||||
uStart int // start of the -u extension.
|
||||
)
|
||||
|
||||
// Generate the tag string if needed.
|
||||
if t.str == "" {
|
||||
uStart = t.genCoreBytes(buf[:])
|
||||
buf[uStart] = '-'
|
||||
uStart++
|
||||
}
|
||||
|
||||
// Create new key-type pair and parse it to verify.
|
||||
b := buf[uStart:]
|
||||
copy(b, "u-")
|
||||
copy(b[2:], key)
|
||||
b[4] = '-'
|
||||
b = b[:5+copy(b[5:], value)]
|
||||
scan := makeScanner(b)
|
||||
if parseExtensions(&scan); scan.err != nil {
|
||||
return t, scan.err
|
||||
}
|
||||
|
||||
// Assemble the replacement string.
|
||||
if t.str == "" {
|
||||
t.pVariant, t.pExt = byte(uStart-1), uint16(uStart-1)
|
||||
t.str = string(buf[:uStart+len(b)])
|
||||
} else {
|
||||
s := t.str
|
||||
start, end, hasExt := t.findTypeForKey(key)
|
||||
if start == end {
|
||||
if hasExt {
|
||||
b = b[2:]
|
||||
}
|
||||
t.str = fmt.Sprintf("%s-%s%s", s[:start], b, s[end:])
|
||||
} else {
|
||||
t.str = fmt.Sprintf("%s%s%s", s[:start], value, s[end:])
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// findKeyAndType returns the start and end position for the type corresponding
|
||||
// to key or the point at which to insert the key-value pair if the type
|
||||
// wasn't found. The hasExt return value reports whether an -u extension was present.
|
||||
// Note: the extensions are typically very small and are likely to contain
|
||||
// only one key-type pair.
|
||||
func (t Tag) findTypeForKey(key string) (start, end int, hasExt bool) {
|
||||
p := int(t.pExt)
|
||||
if len(key) != 2 || p == len(t.str) || p == 0 {
|
||||
return p, p, false
|
||||
}
|
||||
s := t.str
|
||||
|
||||
// Find the correct extension.
|
||||
for p++; s[p] != 'u'; p++ {
|
||||
if s[p] > 'u' {
|
||||
p--
|
||||
return p, p, false
|
||||
}
|
||||
if p = nextExtension(s, p); p == len(s) {
|
||||
return len(s), len(s), false
|
||||
}
|
||||
}
|
||||
// Proceed to the hyphen following the extension name.
|
||||
p++
|
||||
|
||||
// curKey is the key currently being processed.
|
||||
curKey := ""
|
||||
|
||||
// Iterate over keys until we get the end of a section.
|
||||
for {
|
||||
// p points to the hyphen preceding the current token.
|
||||
if p3 := p + 3; s[p3] == '-' {
|
||||
// Found a key.
|
||||
// Check whether we just processed the key that was requested.
|
||||
if curKey == key {
|
||||
return start, p, true
|
||||
}
|
||||
// Set to the next key and continue scanning type tokens.
|
||||
curKey = s[p+1 : p3]
|
||||
if curKey > key {
|
||||
return p, p, true
|
||||
}
|
||||
// Start of the type token sequence.
|
||||
start = p + 4
|
||||
// A type is at least 3 characters long.
|
||||
p += 7 // 4 + 3
|
||||
} else {
|
||||
// Attribute or type, which is at least 3 characters long.
|
||||
p += 4
|
||||
}
|
||||
// p points past the third character of a type or attribute.
|
||||
max := p + 5 // maximum length of token plus hyphen.
|
||||
if len(s) < max {
|
||||
max = len(s)
|
||||
}
|
||||
for ; p < max && s[p] != '-'; p++ {
|
||||
}
|
||||
// Bail if we have exhausted all tokens or if the next token starts
|
||||
// a new extension.
|
||||
if p == len(s) || s[p+2] == '-' {
|
||||
if curKey == key {
|
||||
return start, p, true
|
||||
}
|
||||
return p, p, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ParseBase parses a 2- or 3-letter ISO 639 code.
|
||||
// It returns a ValueError if s is a well-formed but unknown language identifier
|
||||
// or another error if another error occurred.
|
||||
func ParseBase(s string) (Language, error) {
|
||||
if n := len(s); n < 2 || 3 < n {
|
||||
return 0, ErrSyntax
|
||||
}
|
||||
var buf [3]byte
|
||||
return getLangID(buf[:copy(buf[:], s)])
|
||||
}
|
||||
|
||||
// ParseScript parses a 4-letter ISO 15924 code.
|
||||
// It returns a ValueError if s is a well-formed but unknown script identifier
|
||||
// or another error if another error occurred.
|
||||
func ParseScript(s string) (Script, error) {
|
||||
if len(s) != 4 {
|
||||
return 0, ErrSyntax
|
||||
}
|
||||
var buf [4]byte
|
||||
return getScriptID(script, buf[:copy(buf[:], s)])
|
||||
}
|
||||
|
||||
// EncodeM49 returns the Region for the given UN M.49 code.
|
||||
// It returns an error if r is not a valid code.
|
||||
func EncodeM49(r int) (Region, error) {
|
||||
return getRegionM49(r)
|
||||
}
|
||||
|
||||
// ParseRegion parses a 2- or 3-letter ISO 3166-1 or a UN M.49 code.
|
||||
// It returns a ValueError if s is a well-formed but unknown region identifier
|
||||
// or another error if another error occurred.
|
||||
func ParseRegion(s string) (Region, error) {
|
||||
if n := len(s); n < 2 || 3 < n {
|
||||
return 0, ErrSyntax
|
||||
}
|
||||
var buf [3]byte
|
||||
return getRegionID(buf[:copy(buf[:], s)])
|
||||
}
|
||||
|
||||
// IsCountry returns whether this region is a country or autonomous area. This
|
||||
// includes non-standard definitions from CLDR.
|
||||
func (r Region) IsCountry() bool {
|
||||
if r == 0 || r.IsGroup() || r.IsPrivateUse() && r != _XK {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsGroup returns whether this region defines a collection of regions. This
|
||||
// includes non-standard definitions from CLDR.
|
||||
func (r Region) IsGroup() bool {
|
||||
if r == 0 {
|
||||
return false
|
||||
}
|
||||
return int(regionInclusion[r]) < len(regionContainment)
|
||||
}
|
||||
|
||||
// Contains returns whether Region c is contained by Region r. It returns true
|
||||
// if c == r.
|
||||
func (r Region) Contains(c Region) bool {
|
||||
if r == c {
|
||||
return true
|
||||
}
|
||||
g := regionInclusion[r]
|
||||
if g >= nRegionGroups {
|
||||
return false
|
||||
}
|
||||
m := regionContainment[g]
|
||||
|
||||
d := regionInclusion[c]
|
||||
b := regionInclusionBits[d]
|
||||
|
||||
// A contained country may belong to multiple disjoint groups. Matching any
|
||||
// of these indicates containment. If the contained region is a group, it
|
||||
// must strictly be a subset.
|
||||
if d >= nRegionGroups {
|
||||
return b&m != 0
|
||||
}
|
||||
return b&^m == 0
|
||||
}
|
||||
|
||||
var errNoTLD = errors.New("language: region is not a valid ccTLD")
|
||||
|
||||
// TLD returns the country code top-level domain (ccTLD). UK is returned for GB.
|
||||
// In all other cases it returns either the region itself or an error.
|
||||
//
|
||||
// This method may return an error for a region for which there exists a
|
||||
// canonical form with a ccTLD. To get that ccTLD canonicalize r first. The
|
||||
// region will already be canonicalized it was obtained from a Tag that was
|
||||
// obtained using any of the default methods.
|
||||
func (r Region) TLD() (Region, error) {
|
||||
// See http://en.wikipedia.org/wiki/Country_code_top-level_domain for the
|
||||
// difference between ISO 3166-1 and IANA ccTLD.
|
||||
if r == _GB {
|
||||
r = _UK
|
||||
}
|
||||
if (r.typ() & ccTLD) == 0 {
|
||||
return 0, errNoTLD
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Canonicalize returns the region or a possible replacement if the region is
|
||||
// deprecated. It will not return a replacement for deprecated regions that
|
||||
// are split into multiple regions.
|
||||
func (r Region) Canonicalize() Region {
|
||||
if cr := normRegion(r); cr != 0 {
|
||||
return cr
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Variant represents a registered variant of a language as defined by BCP 47.
|
||||
type Variant struct {
|
||||
ID uint8
|
||||
str string
|
||||
}
|
||||
|
||||
// ParseVariant parses and returns a Variant. An error is returned if s is not
|
||||
// a valid variant.
|
||||
func ParseVariant(s string) (Variant, error) {
|
||||
s = strings.ToLower(s)
|
||||
if id, ok := variantIndex[s]; ok {
|
||||
return Variant{id, s}, nil
|
||||
}
|
||||
return Variant{}, NewValueError([]byte(s))
|
||||
}
|
||||
|
||||
// String returns the string representation of the variant.
|
||||
func (v Variant) String() string {
|
||||
return v.str
|
||||
}
|
412
vendor/golang.org/x/text/internal/language/lookup.go
generated
vendored
412
vendor/golang.org/x/text/internal/language/lookup.go
generated
vendored
@ -1,412 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/text/internal/tag"
|
||||
)
|
||||
|
||||
// findIndex tries to find the given tag in idx and returns a standardized error
|
||||
// if it could not be found.
|
||||
func findIndex(idx tag.Index, key []byte, form string) (index int, err error) {
|
||||
if !tag.FixCase(form, key) {
|
||||
return 0, ErrSyntax
|
||||
}
|
||||
i := idx.Index(key)
|
||||
if i == -1 {
|
||||
return 0, NewValueError(key)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func searchUint(imap []uint16, key uint16) int {
|
||||
return sort.Search(len(imap), func(i int) bool {
|
||||
return imap[i] >= key
|
||||
})
|
||||
}
|
||||
|
||||
type Language uint16
|
||||
|
||||
// getLangID returns the langID of s if s is a canonical subtag
|
||||
// or langUnknown if s is not a canonical subtag.
|
||||
func getLangID(s []byte) (Language, error) {
|
||||
if len(s) == 2 {
|
||||
return getLangISO2(s)
|
||||
}
|
||||
return getLangISO3(s)
|
||||
}
|
||||
|
||||
// TODO language normalization as well as the AliasMaps could be moved to the
|
||||
// higher level package, but it is a bit tricky to separate the generation.
|
||||
|
||||
func (id Language) Canonicalize() (Language, AliasType) {
|
||||
return normLang(id)
|
||||
}
|
||||
|
||||
// mapLang returns the mapped langID of id according to mapping m.
|
||||
func normLang(id Language) (Language, AliasType) {
|
||||
k := sort.Search(len(AliasMap), func(i int) bool {
|
||||
return AliasMap[i].From >= uint16(id)
|
||||
})
|
||||
if k < len(AliasMap) && AliasMap[k].From == uint16(id) {
|
||||
return Language(AliasMap[k].To), AliasTypes[k]
|
||||
}
|
||||
return id, AliasTypeUnknown
|
||||
}
|
||||
|
||||
// getLangISO2 returns the langID for the given 2-letter ISO language code
|
||||
// or unknownLang if this does not exist.
|
||||
func getLangISO2(s []byte) (Language, error) {
|
||||
if !tag.FixCase("zz", s) {
|
||||
return 0, ErrSyntax
|
||||
}
|
||||
if i := lang.Index(s); i != -1 && lang.Elem(i)[3] != 0 {
|
||||
return Language(i), nil
|
||||
}
|
||||
return 0, NewValueError(s)
|
||||
}
|
||||
|
||||
const base = 'z' - 'a' + 1
|
||||
|
||||
func strToInt(s []byte) uint {
|
||||
v := uint(0)
|
||||
for i := 0; i < len(s); i++ {
|
||||
v *= base
|
||||
v += uint(s[i] - 'a')
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// converts the given integer to the original ASCII string passed to strToInt.
|
||||
// len(s) must match the number of characters obtained.
|
||||
func intToStr(v uint, s []byte) {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
s[i] = byte(v%base) + 'a'
|
||||
v /= base
|
||||
}
|
||||
}
|
||||
|
||||
// getLangISO3 returns the langID for the given 3-letter ISO language code
|
||||
// or unknownLang if this does not exist.
|
||||
func getLangISO3(s []byte) (Language, error) {
|
||||
if tag.FixCase("und", s) {
|
||||
// first try to match canonical 3-letter entries
|
||||
for i := lang.Index(s[:2]); i != -1; i = lang.Next(s[:2], i) {
|
||||
if e := lang.Elem(i); e[3] == 0 && e[2] == s[2] {
|
||||
// We treat "und" as special and always translate it to "unspecified".
|
||||
// Note that ZZ and Zzzz are private use and are not treated as
|
||||
// unspecified by default.
|
||||
id := Language(i)
|
||||
if id == nonCanonicalUnd {
|
||||
return 0, nil
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
if i := altLangISO3.Index(s); i != -1 {
|
||||
return Language(altLangIndex[altLangISO3.Elem(i)[3]]), nil
|
||||
}
|
||||
n := strToInt(s)
|
||||
if langNoIndex[n/8]&(1<<(n%8)) != 0 {
|
||||
return Language(n) + langNoIndexOffset, nil
|
||||
}
|
||||
// Check for non-canonical uses of ISO3.
|
||||
for i := lang.Index(s[:1]); i != -1; i = lang.Next(s[:1], i) {
|
||||
if e := lang.Elem(i); e[2] == s[1] && e[3] == s[2] {
|
||||
return Language(i), nil
|
||||
}
|
||||
}
|
||||
return 0, NewValueError(s)
|
||||
}
|
||||
return 0, ErrSyntax
|
||||
}
|
||||
|
||||
// StringToBuf writes the string to b and returns the number of bytes
|
||||
// written. cap(b) must be >= 3.
|
||||
func (id Language) StringToBuf(b []byte) int {
|
||||
if id >= langNoIndexOffset {
|
||||
intToStr(uint(id)-langNoIndexOffset, b[:3])
|
||||
return 3
|
||||
} else if id == 0 {
|
||||
return copy(b, "und")
|
||||
}
|
||||
l := lang[id<<2:]
|
||||
if l[3] == 0 {
|
||||
return copy(b, l[:3])
|
||||
}
|
||||
return copy(b, l[:2])
|
||||
}
|
||||
|
||||
// String returns the BCP 47 representation of the langID.
|
||||
// Use b as variable name, instead of id, to ensure the variable
|
||||
// used is consistent with that of Base in which this type is embedded.
|
||||
func (b Language) String() string {
|
||||
if b == 0 {
|
||||
return "und"
|
||||
} else if b >= langNoIndexOffset {
|
||||
b -= langNoIndexOffset
|
||||
buf := [3]byte{}
|
||||
intToStr(uint(b), buf[:])
|
||||
return string(buf[:])
|
||||
}
|
||||
l := lang.Elem(int(b))
|
||||
if l[3] == 0 {
|
||||
return l[:3]
|
||||
}
|
||||
return l[:2]
|
||||
}
|
||||
|
||||
// ISO3 returns the ISO 639-3 language code.
|
||||
func (b Language) ISO3() string {
|
||||
if b == 0 || b >= langNoIndexOffset {
|
||||
return b.String()
|
||||
}
|
||||
l := lang.Elem(int(b))
|
||||
if l[3] == 0 {
|
||||
return l[:3]
|
||||
} else if l[2] == 0 {
|
||||
return altLangISO3.Elem(int(l[3]))[:3]
|
||||
}
|
||||
// This allocation will only happen for 3-letter ISO codes
|
||||
// that are non-canonical BCP 47 language identifiers.
|
||||
return l[0:1] + l[2:4]
|
||||
}
|
||||
|
||||
// IsPrivateUse reports whether this language code is reserved for private use.
|
||||
func (b Language) IsPrivateUse() bool {
|
||||
return langPrivateStart <= b && b <= langPrivateEnd
|
||||
}
|
||||
|
||||
// SuppressScript returns the script marked as SuppressScript in the IANA
|
||||
// language tag repository, or 0 if there is no such script.
|
||||
func (b Language) SuppressScript() Script {
|
||||
if b < langNoIndexOffset {
|
||||
return Script(suppressScript[b])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Region uint16
|
||||
|
||||
// getRegionID returns the region id for s if s is a valid 2-letter region code
|
||||
// or unknownRegion.
|
||||
func getRegionID(s []byte) (Region, error) {
|
||||
if len(s) == 3 {
|
||||
if isAlpha(s[0]) {
|
||||
return getRegionISO3(s)
|
||||
}
|
||||
if i, err := strconv.ParseUint(string(s), 10, 10); err == nil {
|
||||
return getRegionM49(int(i))
|
||||
}
|
||||
}
|
||||
return getRegionISO2(s)
|
||||
}
|
||||
|
||||
// getRegionISO2 returns the regionID for the given 2-letter ISO country code
|
||||
// or unknownRegion if this does not exist.
|
||||
func getRegionISO2(s []byte) (Region, error) {
|
||||
i, err := findIndex(regionISO, s, "ZZ")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return Region(i) + isoRegionOffset, nil
|
||||
}
|
||||
|
||||
// getRegionISO3 returns the regionID for the given 3-letter ISO country code
|
||||
// or unknownRegion if this does not exist.
|
||||
func getRegionISO3(s []byte) (Region, error) {
|
||||
if tag.FixCase("ZZZ", s) {
|
||||
for i := regionISO.Index(s[:1]); i != -1; i = regionISO.Next(s[:1], i) {
|
||||
if e := regionISO.Elem(i); e[2] == s[1] && e[3] == s[2] {
|
||||
return Region(i) + isoRegionOffset, nil
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(altRegionISO3); i += 3 {
|
||||
if tag.Compare(altRegionISO3[i:i+3], s) == 0 {
|
||||
return Region(altRegionIDs[i/3]), nil
|
||||
}
|
||||
}
|
||||
return 0, NewValueError(s)
|
||||
}
|
||||
return 0, ErrSyntax
|
||||
}
|
||||
|
||||
func getRegionM49(n int) (Region, error) {
|
||||
if 0 < n && n <= 999 {
|
||||
const (
|
||||
searchBits = 7
|
||||
regionBits = 9
|
||||
regionMask = 1<<regionBits - 1
|
||||
)
|
||||
idx := n >> searchBits
|
||||
buf := fromM49[m49Index[idx]:m49Index[idx+1]]
|
||||
val := uint16(n) << regionBits // we rely on bits shifting out
|
||||
i := sort.Search(len(buf), func(i int) bool {
|
||||
return buf[i] >= val
|
||||
})
|
||||
if r := fromM49[int(m49Index[idx])+i]; r&^regionMask == val {
|
||||
return Region(r & regionMask), nil
|
||||
}
|
||||
}
|
||||
var e ValueError
|
||||
fmt.Fprint(bytes.NewBuffer([]byte(e.v[:])), n)
|
||||
return 0, e
|
||||
}
|
||||
|
||||
// normRegion returns a region if r is deprecated or 0 otherwise.
|
||||
// TODO: consider supporting BYS (-> BLR), CSK (-> 200 or CZ), PHI (-> PHL) and AFI (-> DJ).
|
||||
// TODO: consider mapping split up regions to new most populous one (like CLDR).
|
||||
func normRegion(r Region) Region {
|
||||
m := regionOldMap
|
||||
k := sort.Search(len(m), func(i int) bool {
|
||||
return m[i].From >= uint16(r)
|
||||
})
|
||||
if k < len(m) && m[k].From == uint16(r) {
|
||||
return Region(m[k].To)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
const (
|
||||
iso3166UserAssigned = 1 << iota
|
||||
ccTLD
|
||||
bcp47Region
|
||||
)
|
||||
|
||||
func (r Region) typ() byte {
|
||||
return regionTypes[r]
|
||||
}
|
||||
|
||||
// String returns the BCP 47 representation for the region.
|
||||
// It returns "ZZ" for an unspecified region.
|
||||
func (r Region) String() string {
|
||||
if r < isoRegionOffset {
|
||||
if r == 0 {
|
||||
return "ZZ"
|
||||
}
|
||||
return fmt.Sprintf("%03d", r.M49())
|
||||
}
|
||||
r -= isoRegionOffset
|
||||
return regionISO.Elem(int(r))[:2]
|
||||
}
|
||||
|
||||
// ISO3 returns the 3-letter ISO code of r.
|
||||
// Note that not all regions have a 3-letter ISO code.
|
||||
// In such cases this method returns "ZZZ".
|
||||
func (r Region) ISO3() string {
|
||||
if r < isoRegionOffset {
|
||||
return "ZZZ"
|
||||
}
|
||||
r -= isoRegionOffset
|
||||
reg := regionISO.Elem(int(r))
|
||||
switch reg[2] {
|
||||
case 0:
|
||||
return altRegionISO3[reg[3]:][:3]
|
||||
case ' ':
|
||||
return "ZZZ"
|
||||
}
|
||||
return reg[0:1] + reg[2:4]
|
||||
}
|
||||
|
||||
// M49 returns the UN M.49 encoding of r, or 0 if this encoding
|
||||
// is not defined for r.
|
||||
func (r Region) M49() int {
|
||||
return int(m49[r])
|
||||
}
|
||||
|
||||
// IsPrivateUse reports whether r has the ISO 3166 User-assigned status. This
|
||||
// may include private-use tags that are assigned by CLDR and used in this
|
||||
// implementation. So IsPrivateUse and IsCountry can be simultaneously true.
|
||||
func (r Region) IsPrivateUse() bool {
|
||||
return r.typ()&iso3166UserAssigned != 0
|
||||
}
|
||||
|
||||
type Script uint8
|
||||
|
||||
// getScriptID returns the script id for string s. It assumes that s
|
||||
// is of the format [A-Z][a-z]{3}.
|
||||
func getScriptID(idx tag.Index, s []byte) (Script, error) {
|
||||
i, err := findIndex(idx, s, "Zzzz")
|
||||
return Script(i), err
|
||||
}
|
||||
|
||||
// String returns the script code in title case.
|
||||
// It returns "Zzzz" for an unspecified script.
|
||||
func (s Script) String() string {
|
||||
if s == 0 {
|
||||
return "Zzzz"
|
||||
}
|
||||
return script.Elem(int(s))
|
||||
}
|
||||
|
||||
// IsPrivateUse reports whether this script code is reserved for private use.
|
||||
func (s Script) IsPrivateUse() bool {
|
||||
return _Qaaa <= s && s <= _Qabx
|
||||
}
|
||||
|
||||
const (
|
||||
maxAltTaglen = len("en-US-POSIX")
|
||||
maxLen = maxAltTaglen
|
||||
)
|
||||
|
||||
var (
|
||||
// grandfatheredMap holds a mapping from legacy and grandfathered tags to
|
||||
// their base language or index to more elaborate tag.
|
||||
grandfatheredMap = map[[maxLen]byte]int16{
|
||||
[maxLen]byte{'a', 'r', 't', '-', 'l', 'o', 'j', 'b', 'a', 'n'}: _jbo, // art-lojban
|
||||
[maxLen]byte{'i', '-', 'a', 'm', 'i'}: _ami, // i-ami
|
||||
[maxLen]byte{'i', '-', 'b', 'n', 'n'}: _bnn, // i-bnn
|
||||
[maxLen]byte{'i', '-', 'h', 'a', 'k'}: _hak, // i-hak
|
||||
[maxLen]byte{'i', '-', 'k', 'l', 'i', 'n', 'g', 'o', 'n'}: _tlh, // i-klingon
|
||||
[maxLen]byte{'i', '-', 'l', 'u', 'x'}: _lb, // i-lux
|
||||
[maxLen]byte{'i', '-', 'n', 'a', 'v', 'a', 'j', 'o'}: _nv, // i-navajo
|
||||
[maxLen]byte{'i', '-', 'p', 'w', 'n'}: _pwn, // i-pwn
|
||||
[maxLen]byte{'i', '-', 't', 'a', 'o'}: _tao, // i-tao
|
||||
[maxLen]byte{'i', '-', 't', 'a', 'y'}: _tay, // i-tay
|
||||
[maxLen]byte{'i', '-', 't', 's', 'u'}: _tsu, // i-tsu
|
||||
[maxLen]byte{'n', 'o', '-', 'b', 'o', 'k'}: _nb, // no-bok
|
||||
[maxLen]byte{'n', 'o', '-', 'n', 'y', 'n'}: _nn, // no-nyn
|
||||
[maxLen]byte{'s', 'g', 'n', '-', 'b', 'e', '-', 'f', 'r'}: _sfb, // sgn-BE-FR
|
||||
[maxLen]byte{'s', 'g', 'n', '-', 'b', 'e', '-', 'n', 'l'}: _vgt, // sgn-BE-NL
|
||||
[maxLen]byte{'s', 'g', 'n', '-', 'c', 'h', '-', 'd', 'e'}: _sgg, // sgn-CH-DE
|
||||
[maxLen]byte{'z', 'h', '-', 'g', 'u', 'o', 'y', 'u'}: _cmn, // zh-guoyu
|
||||
[maxLen]byte{'z', 'h', '-', 'h', 'a', 'k', 'k', 'a'}: _hak, // zh-hakka
|
||||
[maxLen]byte{'z', 'h', '-', 'm', 'i', 'n', '-', 'n', 'a', 'n'}: _nan, // zh-min-nan
|
||||
[maxLen]byte{'z', 'h', '-', 'x', 'i', 'a', 'n', 'g'}: _hsn, // zh-xiang
|
||||
|
||||
// Grandfathered tags with no modern replacement will be converted as
|
||||
// follows:
|
||||
[maxLen]byte{'c', 'e', 'l', '-', 'g', 'a', 'u', 'l', 'i', 's', 'h'}: -1, // cel-gaulish
|
||||
[maxLen]byte{'e', 'n', '-', 'g', 'b', '-', 'o', 'e', 'd'}: -2, // en-GB-oed
|
||||
[maxLen]byte{'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'}: -3, // i-default
|
||||
[maxLen]byte{'i', '-', 'e', 'n', 'o', 'c', 'h', 'i', 'a', 'n'}: -4, // i-enochian
|
||||
[maxLen]byte{'i', '-', 'm', 'i', 'n', 'g', 'o'}: -5, // i-mingo
|
||||
[maxLen]byte{'z', 'h', '-', 'm', 'i', 'n'}: -6, // zh-min
|
||||
|
||||
// CLDR-specific tag.
|
||||
[maxLen]byte{'r', 'o', 'o', 't'}: 0, // root
|
||||
[maxLen]byte{'e', 'n', '-', 'u', 's', '-', 'p', 'o', 's', 'i', 'x'}: -7, // en_US_POSIX"
|
||||
}
|
||||
|
||||
altTagIndex = [...]uint8{0, 17, 31, 45, 61, 74, 86, 102}
|
||||
|
||||
altTags = "xtg-x-cel-gaulishen-GB-oxendicten-x-i-defaultund-x-i-enochiansee-x-i-mingonan-x-zh-minen-US-u-va-posix"
|
||||
)
|
||||
|
||||
func grandfathered(s [maxAltTaglen]byte) (t Tag, ok bool) {
|
||||
if v, ok := grandfatheredMap[s]; ok {
|
||||
if v < 0 {
|
||||
return Make(altTags[altTagIndex[-v-1]:altTagIndex[-v]]), true
|
||||
}
|
||||
t.LangID = Language(v)
|
||||
return t, true
|
||||
}
|
||||
return t, false
|
||||
}
|
226
vendor/golang.org/x/text/internal/language/match.go
generated
vendored
226
vendor/golang.org/x/text/internal/language/match.go
generated
vendored
@ -1,226 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
import "errors"
|
||||
|
||||
type scriptRegionFlags uint8
|
||||
|
||||
const (
|
||||
isList = 1 << iota
|
||||
scriptInFrom
|
||||
regionInFrom
|
||||
)
|
||||
|
||||
func (t *Tag) setUndefinedLang(id Language) {
|
||||
if t.LangID == 0 {
|
||||
t.LangID = id
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tag) setUndefinedScript(id Script) {
|
||||
if t.ScriptID == 0 {
|
||||
t.ScriptID = id
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tag) setUndefinedRegion(id Region) {
|
||||
if t.RegionID == 0 || t.RegionID.Contains(id) {
|
||||
t.RegionID = id
|
||||
}
|
||||
}
|
||||
|
||||
// ErrMissingLikelyTagsData indicates no information was available
|
||||
// to compute likely values of missing tags.
|
||||
var ErrMissingLikelyTagsData = errors.New("missing likely tags data")
|
||||
|
||||
// addLikelySubtags sets subtags to their most likely value, given the locale.
|
||||
// In most cases this means setting fields for unknown values, but in some
|
||||
// cases it may alter a value. It returns an ErrMissingLikelyTagsData error
|
||||
// if the given locale cannot be expanded.
|
||||
func (t Tag) addLikelySubtags() (Tag, error) {
|
||||
id, err := addTags(t)
|
||||
if err != nil {
|
||||
return t, err
|
||||
} else if id.equalTags(t) {
|
||||
return t, nil
|
||||
}
|
||||
id.RemakeString()
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// specializeRegion attempts to specialize a group region.
|
||||
func specializeRegion(t *Tag) bool {
|
||||
if i := regionInclusion[t.RegionID]; i < nRegionGroups {
|
||||
x := likelyRegionGroup[i]
|
||||
if Language(x.lang) == t.LangID && Script(x.script) == t.ScriptID {
|
||||
t.RegionID = Region(x.region)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Maximize returns a new tag with missing tags filled in.
|
||||
func (t Tag) Maximize() (Tag, error) {
|
||||
return addTags(t)
|
||||
}
|
||||
|
||||
func addTags(t Tag) (Tag, error) {
|
||||
// We leave private use identifiers alone.
|
||||
if t.IsPrivateUse() {
|
||||
return t, nil
|
||||
}
|
||||
if t.ScriptID != 0 && t.RegionID != 0 {
|
||||
if t.LangID != 0 {
|
||||
// already fully specified
|
||||
specializeRegion(&t)
|
||||
return t, nil
|
||||
}
|
||||
// Search matches for und-script-region. Note that for these cases
|
||||
// region will never be a group so there is no need to check for this.
|
||||
list := likelyRegion[t.RegionID : t.RegionID+1]
|
||||
if x := list[0]; x.flags&isList != 0 {
|
||||
list = likelyRegionList[x.lang : x.lang+uint16(x.script)]
|
||||
}
|
||||
for _, x := range list {
|
||||
// Deviating from the spec. See match_test.go for details.
|
||||
if Script(x.script) == t.ScriptID {
|
||||
t.setUndefinedLang(Language(x.lang))
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.LangID != 0 {
|
||||
// Search matches for lang-script and lang-region, where lang != und.
|
||||
if t.LangID < langNoIndexOffset {
|
||||
x := likelyLang[t.LangID]
|
||||
if x.flags&isList != 0 {
|
||||
list := likelyLangList[x.region : x.region+uint16(x.script)]
|
||||
if t.ScriptID != 0 {
|
||||
for _, x := range list {
|
||||
if Script(x.script) == t.ScriptID && x.flags&scriptInFrom != 0 {
|
||||
t.setUndefinedRegion(Region(x.region))
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
} else if t.RegionID != 0 {
|
||||
count := 0
|
||||
goodScript := true
|
||||
tt := t
|
||||
for _, x := range list {
|
||||
// We visit all entries for which the script was not
|
||||
// defined, including the ones where the region was not
|
||||
// defined. This allows for proper disambiguation within
|
||||
// regions.
|
||||
if x.flags&scriptInFrom == 0 && t.RegionID.Contains(Region(x.region)) {
|
||||
tt.RegionID = Region(x.region)
|
||||
tt.setUndefinedScript(Script(x.script))
|
||||
goodScript = goodScript && tt.ScriptID == Script(x.script)
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == 1 {
|
||||
return tt, nil
|
||||
}
|
||||
// Even if we fail to find a unique Region, we might have
|
||||
// an unambiguous script.
|
||||
if goodScript {
|
||||
t.ScriptID = tt.ScriptID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Search matches for und-script.
|
||||
if t.ScriptID != 0 {
|
||||
x := likelyScript[t.ScriptID]
|
||||
if x.region != 0 {
|
||||
t.setUndefinedRegion(Region(x.region))
|
||||
t.setUndefinedLang(Language(x.lang))
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
// Search matches for und-region. If und-script-region exists, it would
|
||||
// have been found earlier.
|
||||
if t.RegionID != 0 {
|
||||
if i := regionInclusion[t.RegionID]; i < nRegionGroups {
|
||||
x := likelyRegionGroup[i]
|
||||
if x.region != 0 {
|
||||
t.setUndefinedLang(Language(x.lang))
|
||||
t.setUndefinedScript(Script(x.script))
|
||||
t.RegionID = Region(x.region)
|
||||
}
|
||||
} else {
|
||||
x := likelyRegion[t.RegionID]
|
||||
if x.flags&isList != 0 {
|
||||
x = likelyRegionList[x.lang]
|
||||
}
|
||||
if x.script != 0 && x.flags != scriptInFrom {
|
||||
t.setUndefinedLang(Language(x.lang))
|
||||
t.setUndefinedScript(Script(x.script))
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search matches for lang.
|
||||
if t.LangID < langNoIndexOffset {
|
||||
x := likelyLang[t.LangID]
|
||||
if x.flags&isList != 0 {
|
||||
x = likelyLangList[x.region]
|
||||
}
|
||||
if x.region != 0 {
|
||||
t.setUndefinedScript(Script(x.script))
|
||||
t.setUndefinedRegion(Region(x.region))
|
||||
}
|
||||
specializeRegion(&t)
|
||||
if t.LangID == 0 {
|
||||
t.LangID = _en // default language
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
return t, ErrMissingLikelyTagsData
|
||||
}
|
||||
|
||||
func (t *Tag) setTagsFrom(id Tag) {
|
||||
t.LangID = id.LangID
|
||||
t.ScriptID = id.ScriptID
|
||||
t.RegionID = id.RegionID
|
||||
}
|
||||
|
||||
// minimize removes the region or script subtags from t such that
|
||||
// t.addLikelySubtags() == t.minimize().addLikelySubtags().
|
||||
func (t Tag) minimize() (Tag, error) {
|
||||
t, err := minimizeTags(t)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
t.RemakeString()
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// minimizeTags mimics the behavior of the ICU 51 C implementation.
|
||||
func minimizeTags(t Tag) (Tag, error) {
|
||||
if t.equalTags(Und) {
|
||||
return t, nil
|
||||
}
|
||||
max, err := addTags(t)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
for _, id := range [...]Tag{
|
||||
{LangID: t.LangID},
|
||||
{LangID: t.LangID, RegionID: t.RegionID},
|
||||
{LangID: t.LangID, ScriptID: t.ScriptID},
|
||||
} {
|
||||
if x, err := addTags(id); err == nil && max.equalTags(x) {
|
||||
t.setTagsFrom(id)
|
||||
break
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
594
vendor/golang.org/x/text/internal/language/parse.go
generated
vendored
594
vendor/golang.org/x/text/internal/language/parse.go
generated
vendored
@ -1,594 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/text/internal/tag"
|
||||
)
|
||||
|
||||
// isAlpha returns true if the byte is not a digit.
|
||||
// b must be an ASCII letter or digit.
|
||||
func isAlpha(b byte) bool {
|
||||
return b > '9'
|
||||
}
|
||||
|
||||
// isAlphaNum returns true if the string contains only ASCII letters or digits.
|
||||
func isAlphaNum(s []byte) bool {
|
||||
for _, c := range s {
|
||||
if !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ErrSyntax is returned by any of the parsing functions when the
|
||||
// input is not well-formed, according to BCP 47.
|
||||
// TODO: return the position at which the syntax error occurred?
|
||||
var ErrSyntax = errors.New("language: tag is not well-formed")
|
||||
|
||||
// ErrDuplicateKey is returned when a tag contains the same key twice with
|
||||
// different values in the -u section.
|
||||
var ErrDuplicateKey = errors.New("language: different values for same key in -u extension")
|
||||
|
||||
// ValueError is returned by any of the parsing functions when the
|
||||
// input is well-formed but the respective subtag is not recognized
|
||||
// as a valid value.
|
||||
type ValueError struct {
|
||||
v [8]byte
|
||||
}
|
||||
|
||||
// NewValueError creates a new ValueError.
|
||||
func NewValueError(tag []byte) ValueError {
|
||||
var e ValueError
|
||||
copy(e.v[:], tag)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e ValueError) tag() []byte {
|
||||
n := bytes.IndexByte(e.v[:], 0)
|
||||
if n == -1 {
|
||||
n = 8
|
||||
}
|
||||
return e.v[:n]
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e ValueError) Error() string {
|
||||
return fmt.Sprintf("language: subtag %q is well-formed but unknown", e.tag())
|
||||
}
|
||||
|
||||
// Subtag returns the subtag for which the error occurred.
|
||||
func (e ValueError) Subtag() string {
|
||||
return string(e.tag())
|
||||
}
|
||||
|
||||
// scanner is used to scan BCP 47 tokens, which are separated by _ or -.
|
||||
type scanner struct {
|
||||
b []byte
|
||||
bytes [max99thPercentileSize]byte
|
||||
token []byte
|
||||
start int // start position of the current token
|
||||
end int // end position of the current token
|
||||
next int // next point for scan
|
||||
err error
|
||||
done bool
|
||||
}
|
||||
|
||||
func makeScannerString(s string) scanner {
|
||||
scan := scanner{}
|
||||
if len(s) <= len(scan.bytes) {
|
||||
scan.b = scan.bytes[:copy(scan.bytes[:], s)]
|
||||
} else {
|
||||
scan.b = []byte(s)
|
||||
}
|
||||
scan.init()
|
||||
return scan
|
||||
}
|
||||
|
||||
// makeScanner returns a scanner using b as the input buffer.
|
||||
// b is not copied and may be modified by the scanner routines.
|
||||
func makeScanner(b []byte) scanner {
|
||||
scan := scanner{b: b}
|
||||
scan.init()
|
||||
return scan
|
||||
}
|
||||
|
||||
func (s *scanner) init() {
|
||||
for i, c := range s.b {
|
||||
if c == '_' {
|
||||
s.b[i] = '-'
|
||||
}
|
||||
}
|
||||
s.scan()
|
||||
}
|
||||
|
||||
// restToLower converts the string between start and end to lower case.
|
||||
func (s *scanner) toLower(start, end int) {
|
||||
for i := start; i < end; i++ {
|
||||
c := s.b[i]
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
s.b[i] += 'a' - 'A'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *scanner) setError(e error) {
|
||||
if s.err == nil || (e == ErrSyntax && s.err != ErrSyntax) {
|
||||
s.err = e
|
||||
}
|
||||
}
|
||||
|
||||
// resizeRange shrinks or grows the array at position oldStart such that
|
||||
// a new string of size newSize can fit between oldStart and oldEnd.
|
||||
// Sets the scan point to after the resized range.
|
||||
func (s *scanner) resizeRange(oldStart, oldEnd, newSize int) {
|
||||
s.start = oldStart
|
||||
if end := oldStart + newSize; end != oldEnd {
|
||||
diff := end - oldEnd
|
||||
if end < cap(s.b) {
|
||||
b := make([]byte, len(s.b)+diff)
|
||||
copy(b, s.b[:oldStart])
|
||||
copy(b[end:], s.b[oldEnd:])
|
||||
s.b = b
|
||||
} else {
|
||||
s.b = append(s.b[end:], s.b[oldEnd:]...)
|
||||
}
|
||||
s.next = end + (s.next - s.end)
|
||||
s.end = end
|
||||
}
|
||||
}
|
||||
|
||||
// replace replaces the current token with repl.
|
||||
func (s *scanner) replace(repl string) {
|
||||
s.resizeRange(s.start, s.end, len(repl))
|
||||
copy(s.b[s.start:], repl)
|
||||
}
|
||||
|
||||
// gobble removes the current token from the input.
|
||||
// Caller must call scan after calling gobble.
|
||||
func (s *scanner) gobble(e error) {
|
||||
s.setError(e)
|
||||
if s.start == 0 {
|
||||
s.b = s.b[:+copy(s.b, s.b[s.next:])]
|
||||
s.end = 0
|
||||
} else {
|
||||
s.b = s.b[:s.start-1+copy(s.b[s.start-1:], s.b[s.end:])]
|
||||
s.end = s.start - 1
|
||||
}
|
||||
s.next = s.start
|
||||
}
|
||||
|
||||
// deleteRange removes the given range from s.b before the current token.
|
||||
func (s *scanner) deleteRange(start, end int) {
|
||||
s.b = s.b[:start+copy(s.b[start:], s.b[end:])]
|
||||
diff := end - start
|
||||
s.next -= diff
|
||||
s.start -= diff
|
||||
s.end -= diff
|
||||
}
|
||||
|
||||
// scan parses the next token of a BCP 47 string. Tokens that are larger
|
||||
// than 8 characters or include non-alphanumeric characters result in an error
|
||||
// and are gobbled and removed from the output.
|
||||
// It returns the end position of the last token consumed.
|
||||
func (s *scanner) scan() (end int) {
|
||||
end = s.end
|
||||
s.token = nil
|
||||
for s.start = s.next; s.next < len(s.b); {
|
||||
i := bytes.IndexByte(s.b[s.next:], '-')
|
||||
if i == -1 {
|
||||
s.end = len(s.b)
|
||||
s.next = len(s.b)
|
||||
i = s.end - s.start
|
||||
} else {
|
||||
s.end = s.next + i
|
||||
s.next = s.end + 1
|
||||
}
|
||||
token := s.b[s.start:s.end]
|
||||
if i < 1 || i > 8 || !isAlphaNum(token) {
|
||||
s.gobble(ErrSyntax)
|
||||
continue
|
||||
}
|
||||
s.token = token
|
||||
return end
|
||||
}
|
||||
if n := len(s.b); n > 0 && s.b[n-1] == '-' {
|
||||
s.setError(ErrSyntax)
|
||||
s.b = s.b[:len(s.b)-1]
|
||||
}
|
||||
s.done = true
|
||||
return end
|
||||
}
|
||||
|
||||
// acceptMinSize parses multiple tokens of the given size or greater.
|
||||
// It returns the end position of the last token consumed.
|
||||
func (s *scanner) acceptMinSize(min int) (end int) {
|
||||
end = s.end
|
||||
s.scan()
|
||||
for ; len(s.token) >= min; s.scan() {
|
||||
end = s.end
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
// Parse parses the given BCP 47 string and returns a valid Tag. If parsing
|
||||
// failed it returns an error and any part of the tag that could be parsed.
|
||||
// If parsing succeeded but an unknown value was found, it returns
|
||||
// ValueError. The Tag returned in this case is just stripped of the unknown
|
||||
// value. All other values are preserved. It accepts tags in the BCP 47 format
|
||||
// and extensions to this standard defined in
|
||||
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
|
||||
func Parse(s string) (t Tag, err error) {
|
||||
// TODO: consider supporting old-style locale key-value pairs.
|
||||
if s == "" {
|
||||
return Und, ErrSyntax
|
||||
}
|
||||
if len(s) <= maxAltTaglen {
|
||||
b := [maxAltTaglen]byte{}
|
||||
for i, c := range s {
|
||||
// Generating invalid UTF-8 is okay as it won't match.
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
c += 'a' - 'A'
|
||||
} else if c == '_' {
|
||||
c = '-'
|
||||
}
|
||||
b[i] = byte(c)
|
||||
}
|
||||
if t, ok := grandfathered(b); ok {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
scan := makeScannerString(s)
|
||||
return parse(&scan, s)
|
||||
}
|
||||
|
||||
func parse(scan *scanner, s string) (t Tag, err error) {
|
||||
t = Und
|
||||
var end int
|
||||
if n := len(scan.token); n <= 1 {
|
||||
scan.toLower(0, len(scan.b))
|
||||
if n == 0 || scan.token[0] != 'x' {
|
||||
return t, ErrSyntax
|
||||
}
|
||||
end = parseExtensions(scan)
|
||||
} else if n >= 4 {
|
||||
return Und, ErrSyntax
|
||||
} else { // the usual case
|
||||
t, end = parseTag(scan)
|
||||
if n := len(scan.token); n == 1 {
|
||||
t.pExt = uint16(end)
|
||||
end = parseExtensions(scan)
|
||||
} else if end < len(scan.b) {
|
||||
scan.setError(ErrSyntax)
|
||||
scan.b = scan.b[:end]
|
||||
}
|
||||
}
|
||||
if int(t.pVariant) < len(scan.b) {
|
||||
if end < len(s) {
|
||||
s = s[:end]
|
||||
}
|
||||
if len(s) > 0 && tag.Compare(s, scan.b) == 0 {
|
||||
t.str = s
|
||||
} else {
|
||||
t.str = string(scan.b)
|
||||
}
|
||||
} else {
|
||||
t.pVariant, t.pExt = 0, 0
|
||||
}
|
||||
return t, scan.err
|
||||
}
|
||||
|
||||
// parseTag parses language, script, region and variants.
|
||||
// It returns a Tag and the end position in the input that was parsed.
|
||||
func parseTag(scan *scanner) (t Tag, end int) {
|
||||
var e error
|
||||
// TODO: set an error if an unknown lang, script or region is encountered.
|
||||
t.LangID, e = getLangID(scan.token)
|
||||
scan.setError(e)
|
||||
scan.replace(t.LangID.String())
|
||||
langStart := scan.start
|
||||
end = scan.scan()
|
||||
for len(scan.token) == 3 && isAlpha(scan.token[0]) {
|
||||
// From http://tools.ietf.org/html/bcp47, <lang>-<extlang> tags are equivalent
|
||||
// to a tag of the form <extlang>.
|
||||
lang, e := getLangID(scan.token)
|
||||
if lang != 0 {
|
||||
t.LangID = lang
|
||||
copy(scan.b[langStart:], lang.String())
|
||||
scan.b[langStart+3] = '-'
|
||||
scan.start = langStart + 4
|
||||
}
|
||||
scan.gobble(e)
|
||||
end = scan.scan()
|
||||
}
|
||||
if len(scan.token) == 4 && isAlpha(scan.token[0]) {
|
||||
t.ScriptID, e = getScriptID(script, scan.token)
|
||||
if t.ScriptID == 0 {
|
||||
scan.gobble(e)
|
||||
}
|
||||
end = scan.scan()
|
||||
}
|
||||
if n := len(scan.token); n >= 2 && n <= 3 {
|
||||
t.RegionID, e = getRegionID(scan.token)
|
||||
if t.RegionID == 0 {
|
||||
scan.gobble(e)
|
||||
} else {
|
||||
scan.replace(t.RegionID.String())
|
||||
}
|
||||
end = scan.scan()
|
||||
}
|
||||
scan.toLower(scan.start, len(scan.b))
|
||||
t.pVariant = byte(end)
|
||||
end = parseVariants(scan, end, t)
|
||||
t.pExt = uint16(end)
|
||||
return t, end
|
||||
}
|
||||
|
||||
var separator = []byte{'-'}
|
||||
|
||||
// parseVariants scans tokens as long as each token is a valid variant string.
|
||||
// Duplicate variants are removed.
|
||||
func parseVariants(scan *scanner, end int, t Tag) int {
|
||||
start := scan.start
|
||||
varIDBuf := [4]uint8{}
|
||||
variantBuf := [4][]byte{}
|
||||
varID := varIDBuf[:0]
|
||||
variant := variantBuf[:0]
|
||||
last := -1
|
||||
needSort := false
|
||||
for ; len(scan.token) >= 4; scan.scan() {
|
||||
// TODO: measure the impact of needing this conversion and redesign
|
||||
// the data structure if there is an issue.
|
||||
v, ok := variantIndex[string(scan.token)]
|
||||
if !ok {
|
||||
// unknown variant
|
||||
// TODO: allow user-defined variants?
|
||||
scan.gobble(NewValueError(scan.token))
|
||||
continue
|
||||
}
|
||||
varID = append(varID, v)
|
||||
variant = append(variant, scan.token)
|
||||
if !needSort {
|
||||
if last < int(v) {
|
||||
last = int(v)
|
||||
} else {
|
||||
needSort = true
|
||||
// There is no legal combinations of more than 7 variants
|
||||
// (and this is by no means a useful sequence).
|
||||
const maxVariants = 8
|
||||
if len(varID) > maxVariants {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
end = scan.end
|
||||
}
|
||||
if needSort {
|
||||
sort.Sort(variantsSort{varID, variant})
|
||||
k, l := 0, -1
|
||||
for i, v := range varID {
|
||||
w := int(v)
|
||||
if l == w {
|
||||
// Remove duplicates.
|
||||
continue
|
||||
}
|
||||
varID[k] = varID[i]
|
||||
variant[k] = variant[i]
|
||||
k++
|
||||
l = w
|
||||
}
|
||||
if str := bytes.Join(variant[:k], separator); len(str) == 0 {
|
||||
end = start - 1
|
||||
} else {
|
||||
scan.resizeRange(start, end, len(str))
|
||||
copy(scan.b[scan.start:], str)
|
||||
end = scan.end
|
||||
}
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
type variantsSort struct {
|
||||
i []uint8
|
||||
v [][]byte
|
||||
}
|
||||
|
||||
func (s variantsSort) Len() int {
|
||||
return len(s.i)
|
||||
}
|
||||
|
||||
func (s variantsSort) Swap(i, j int) {
|
||||
s.i[i], s.i[j] = s.i[j], s.i[i]
|
||||
s.v[i], s.v[j] = s.v[j], s.v[i]
|
||||
}
|
||||
|
||||
func (s variantsSort) Less(i, j int) bool {
|
||||
return s.i[i] < s.i[j]
|
||||
}
|
||||
|
||||
type bytesSort struct {
|
||||
b [][]byte
|
||||
n int // first n bytes to compare
|
||||
}
|
||||
|
||||
func (b bytesSort) Len() int {
|
||||
return len(b.b)
|
||||
}
|
||||
|
||||
func (b bytesSort) Swap(i, j int) {
|
||||
b.b[i], b.b[j] = b.b[j], b.b[i]
|
||||
}
|
||||
|
||||
func (b bytesSort) Less(i, j int) bool {
|
||||
for k := 0; k < b.n; k++ {
|
||||
if b.b[i][k] == b.b[j][k] {
|
||||
continue
|
||||
}
|
||||
return b.b[i][k] < b.b[j][k]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseExtensions parses and normalizes the extensions in the buffer.
|
||||
// It returns the last position of scan.b that is part of any extension.
|
||||
// It also trims scan.b to remove excess parts accordingly.
|
||||
func parseExtensions(scan *scanner) int {
|
||||
start := scan.start
|
||||
exts := [][]byte{}
|
||||
private := []byte{}
|
||||
end := scan.end
|
||||
for len(scan.token) == 1 {
|
||||
extStart := scan.start
|
||||
ext := scan.token[0]
|
||||
end = parseExtension(scan)
|
||||
extension := scan.b[extStart:end]
|
||||
if len(extension) < 3 || (ext != 'x' && len(extension) < 4) {
|
||||
scan.setError(ErrSyntax)
|
||||
end = extStart
|
||||
continue
|
||||
} else if start == extStart && (ext == 'x' || scan.start == len(scan.b)) {
|
||||
scan.b = scan.b[:end]
|
||||
return end
|
||||
} else if ext == 'x' {
|
||||
private = extension
|
||||
break
|
||||
}
|
||||
exts = append(exts, extension)
|
||||
}
|
||||
sort.Sort(bytesSort{exts, 1})
|
||||
if len(private) > 0 {
|
||||
exts = append(exts, private)
|
||||
}
|
||||
scan.b = scan.b[:start]
|
||||
if len(exts) > 0 {
|
||||
scan.b = append(scan.b, bytes.Join(exts, separator)...)
|
||||
} else if start > 0 {
|
||||
// Strip trailing '-'.
|
||||
scan.b = scan.b[:start-1]
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
// parseExtension parses a single extension and returns the position of
|
||||
// the extension end.
|
||||
func parseExtension(scan *scanner) int {
|
||||
start, end := scan.start, scan.end
|
||||
switch scan.token[0] {
|
||||
case 'u':
|
||||
attrStart := end
|
||||
scan.scan()
|
||||
for last := []byte{}; len(scan.token) > 2; scan.scan() {
|
||||
if bytes.Compare(scan.token, last) != -1 {
|
||||
// Attributes are unsorted. Start over from scratch.
|
||||
p := attrStart + 1
|
||||
scan.next = p
|
||||
attrs := [][]byte{}
|
||||
for scan.scan(); len(scan.token) > 2; scan.scan() {
|
||||
attrs = append(attrs, scan.token)
|
||||
end = scan.end
|
||||
}
|
||||
sort.Sort(bytesSort{attrs, 3})
|
||||
copy(scan.b[p:], bytes.Join(attrs, separator))
|
||||
break
|
||||
}
|
||||
last = scan.token
|
||||
end = scan.end
|
||||
}
|
||||
var last, key []byte
|
||||
for attrEnd := end; len(scan.token) == 2; last = key {
|
||||
key = scan.token
|
||||
keyEnd := scan.end
|
||||
end = scan.acceptMinSize(3)
|
||||
// TODO: check key value validity
|
||||
if keyEnd == end || bytes.Compare(key, last) != 1 {
|
||||
// We have an invalid key or the keys are not sorted.
|
||||
// Start scanning keys from scratch and reorder.
|
||||
p := attrEnd + 1
|
||||
scan.next = p
|
||||
keys := [][]byte{}
|
||||
for scan.scan(); len(scan.token) == 2; {
|
||||
keyStart, keyEnd := scan.start, scan.end
|
||||
end = scan.acceptMinSize(3)
|
||||
if keyEnd != end {
|
||||
keys = append(keys, scan.b[keyStart:end])
|
||||
} else {
|
||||
scan.setError(ErrSyntax)
|
||||
end = keyStart
|
||||
}
|
||||
}
|
||||
sort.Stable(bytesSort{keys, 2})
|
||||
if n := len(keys); n > 0 {
|
||||
k := 0
|
||||
for i := 1; i < n; i++ {
|
||||
if !bytes.Equal(keys[k][:2], keys[i][:2]) {
|
||||
k++
|
||||
keys[k] = keys[i]
|
||||
} else if !bytes.Equal(keys[k], keys[i]) {
|
||||
scan.setError(ErrDuplicateKey)
|
||||
}
|
||||
}
|
||||
keys = keys[:k+1]
|
||||
}
|
||||
reordered := bytes.Join(keys, separator)
|
||||
if e := p + len(reordered); e < end {
|
||||
scan.deleteRange(e, end)
|
||||
end = e
|
||||
}
|
||||
copy(scan.b[p:], reordered)
|
||||
break
|
||||
}
|
||||
}
|
||||
case 't':
|
||||
scan.scan()
|
||||
if n := len(scan.token); n >= 2 && n <= 3 && isAlpha(scan.token[1]) {
|
||||
_, end = parseTag(scan)
|
||||
scan.toLower(start, end)
|
||||
}
|
||||
for len(scan.token) == 2 && !isAlpha(scan.token[1]) {
|
||||
end = scan.acceptMinSize(3)
|
||||
}
|
||||
case 'x':
|
||||
end = scan.acceptMinSize(1)
|
||||
default:
|
||||
end = scan.acceptMinSize(2)
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
// getExtension returns the name, body and end position of the extension.
|
||||
func getExtension(s string, p int) (end int, ext string) {
|
||||
if s[p] == '-' {
|
||||
p++
|
||||
}
|
||||
if s[p] == 'x' {
|
||||
return len(s), s[p:]
|
||||
}
|
||||
end = nextExtension(s, p)
|
||||
return end, s[p:end]
|
||||
}
|
||||
|
||||
// nextExtension finds the next extension within the string, searching
|
||||
// for the -<char>- pattern from position p.
|
||||
// In the fast majority of cases, language tags will have at most
|
||||
// one extension and extensions tend to be small.
|
||||
func nextExtension(s string, p int) int {
|
||||
for n := len(s) - 3; p < n; {
|
||||
if s[p] == '-' {
|
||||
if s[p+2] == '-' {
|
||||
return p
|
||||
}
|
||||
p += 3
|
||||
} else {
|
||||
p++
|
||||
}
|
||||
}
|
||||
return len(s)
|
||||
}
|
3431
vendor/golang.org/x/text/internal/language/tables.go
generated
vendored
3431
vendor/golang.org/x/text/internal/language/tables.go
generated
vendored
File diff suppressed because it is too large
Load Diff
48
vendor/golang.org/x/text/internal/language/tags.go
generated
vendored
48
vendor/golang.org/x/text/internal/language/tags.go
generated
vendored
@ -1,48 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
// MustParse is like Parse, but panics if the given BCP 47 tag cannot be parsed.
|
||||
// It simplifies safe initialization of Tag values.
|
||||
func MustParse(s string) Tag {
|
||||
t, err := Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// MustParseBase is like ParseBase, but panics if the given base cannot be parsed.
|
||||
// It simplifies safe initialization of Base values.
|
||||
func MustParseBase(s string) Language {
|
||||
b, err := ParseBase(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// MustParseScript is like ParseScript, but panics if the given script cannot be
|
||||
// parsed. It simplifies safe initialization of Script values.
|
||||
func MustParseScript(s string) Script {
|
||||
scr, err := ParseScript(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return scr
|
||||
}
|
||||
|
||||
// MustParseRegion is like ParseRegion, but panics if the given region cannot be
|
||||
// parsed. It simplifies safe initialization of Region values.
|
||||
func MustParseRegion(s string) Region {
|
||||
r, err := ParseRegion(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Und is the root language.
|
||||
var Und Tag
|
100
vendor/golang.org/x/text/internal/tag/tag.go
generated
vendored
100
vendor/golang.org/x/text/internal/tag/tag.go
generated
vendored
@ -1,100 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package tag contains functionality handling tags and related data.
|
||||
package tag // import "golang.org/x/text/internal/tag"
|
||||
|
||||
import "sort"
|
||||
|
||||
// An Index converts tags to a compact numeric value.
|
||||
//
|
||||
// All elements are of size 4. Tags may be up to 4 bytes long. Excess bytes can
|
||||
// be used to store additional information about the tag.
|
||||
type Index string
|
||||
|
||||
// Elem returns the element data at the given index.
|
||||
func (s Index) Elem(x int) string {
|
||||
return string(s[x*4 : x*4+4])
|
||||
}
|
||||
|
||||
// Index reports the index of the given key or -1 if it could not be found.
|
||||
// Only the first len(key) bytes from the start of the 4-byte entries will be
|
||||
// considered for the search and the first match in Index will be returned.
|
||||
func (s Index) Index(key []byte) int {
|
||||
n := len(key)
|
||||
// search the index of the first entry with an equal or higher value than
|
||||
// key in s.
|
||||
index := sort.Search(len(s)/4, func(i int) bool {
|
||||
return cmp(s[i*4:i*4+n], key) != -1
|
||||
})
|
||||
i := index * 4
|
||||
if cmp(s[i:i+len(key)], key) != 0 {
|
||||
return -1
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// Next finds the next occurrence of key after index x, which must have been
|
||||
// obtained from a call to Index using the same key. It returns x+1 or -1.
|
||||
func (s Index) Next(key []byte, x int) int {
|
||||
if x++; x*4 < len(s) && cmp(s[x*4:x*4+len(key)], key) == 0 {
|
||||
return x
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// cmp returns an integer comparing a and b lexicographically.
|
||||
func cmp(a Index, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
for i, c := range b[:n] {
|
||||
switch {
|
||||
case a[i] > c:
|
||||
return 1
|
||||
case a[i] < c:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case len(a) < len(b):
|
||||
return -1
|
||||
case len(a) > len(b):
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Compare returns an integer comparing a and b lexicographically.
|
||||
func Compare(a string, b []byte) int {
|
||||
return cmp(Index(a), b)
|
||||
}
|
||||
|
||||
// FixCase reformats b to the same pattern of cases as form.
|
||||
// If returns false if string b is malformed.
|
||||
func FixCase(form string, b []byte) bool {
|
||||
if len(form) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, c := range b {
|
||||
if form[i] <= 'Z' {
|
||||
if c >= 'a' {
|
||||
c -= 'z' - 'Z'
|
||||
}
|
||||
if c < 'A' || 'Z' < c {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if c <= 'Z' {
|
||||
c += 'z' - 'Z'
|
||||
}
|
||||
if c < 'a' || 'z' < c {
|
||||
return false
|
||||
}
|
||||
}
|
||||
b[i] = c
|
||||
}
|
||||
return true
|
||||
}
|
187
vendor/golang.org/x/text/language/coverage.go
generated
vendored
187
vendor/golang.org/x/text/language/coverage.go
generated
vendored
@ -1,187 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/text/internal/language"
|
||||
)
|
||||
|
||||
// The Coverage interface is used to define the level of coverage of an
|
||||
// internationalization service. Note that not all types are supported by all
|
||||
// services. As lists may be generated on the fly, it is recommended that users
|
||||
// of a Coverage cache the results.
|
||||
type Coverage interface {
|
||||
// Tags returns the list of supported tags.
|
||||
Tags() []Tag
|
||||
|
||||
// BaseLanguages returns the list of supported base languages.
|
||||
BaseLanguages() []Base
|
||||
|
||||
// Scripts returns the list of supported scripts.
|
||||
Scripts() []Script
|
||||
|
||||
// Regions returns the list of supported regions.
|
||||
Regions() []Region
|
||||
}
|
||||
|
||||
var (
|
||||
// Supported defines a Coverage that lists all supported subtags. Tags
|
||||
// always returns nil.
|
||||
Supported Coverage = allSubtags{}
|
||||
)
|
||||
|
||||
// TODO:
|
||||
// - Support Variants, numbering systems.
|
||||
// - CLDR coverage levels.
|
||||
// - Set of common tags defined in this package.
|
||||
|
||||
type allSubtags struct{}
|
||||
|
||||
// Regions returns the list of supported regions. As all regions are in a
|
||||
// consecutive range, it simply returns a slice of numbers in increasing order.
|
||||
// The "undefined" region is not returned.
|
||||
func (s allSubtags) Regions() []Region {
|
||||
reg := make([]Region, language.NumRegions)
|
||||
for i := range reg {
|
||||
reg[i] = Region{language.Region(i + 1)}
|
||||
}
|
||||
return reg
|
||||
}
|
||||
|
||||
// Scripts returns the list of supported scripts. As all scripts are in a
|
||||
// consecutive range, it simply returns a slice of numbers in increasing order.
|
||||
// The "undefined" script is not returned.
|
||||
func (s allSubtags) Scripts() []Script {
|
||||
scr := make([]Script, language.NumScripts)
|
||||
for i := range scr {
|
||||
scr[i] = Script{language.Script(i + 1)}
|
||||
}
|
||||
return scr
|
||||
}
|
||||
|
||||
// BaseLanguages returns the list of all supported base languages. It generates
|
||||
// the list by traversing the internal structures.
|
||||
func (s allSubtags) BaseLanguages() []Base {
|
||||
bs := language.BaseLanguages()
|
||||
base := make([]Base, len(bs))
|
||||
for i, b := range bs {
|
||||
base[i] = Base{b}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// Tags always returns nil.
|
||||
func (s allSubtags) Tags() []Tag {
|
||||
return nil
|
||||
}
|
||||
|
||||
// coverage is used by NewCoverage which is used as a convenient way for
|
||||
// creating Coverage implementations for partially defined data. Very often a
|
||||
// package will only need to define a subset of slices. coverage provides a
|
||||
// convenient way to do this. Moreover, packages using NewCoverage, instead of
|
||||
// their own implementation, will not break if later new slice types are added.
|
||||
type coverage struct {
|
||||
tags func() []Tag
|
||||
bases func() []Base
|
||||
scripts func() []Script
|
||||
regions func() []Region
|
||||
}
|
||||
|
||||
func (s *coverage) Tags() []Tag {
|
||||
if s.tags == nil {
|
||||
return nil
|
||||
}
|
||||
return s.tags()
|
||||
}
|
||||
|
||||
// bases implements sort.Interface and is used to sort base languages.
|
||||
type bases []Base
|
||||
|
||||
func (b bases) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b bases) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
func (b bases) Less(i, j int) bool {
|
||||
return b[i].langID < b[j].langID
|
||||
}
|
||||
|
||||
// BaseLanguages returns the result from calling s.bases if it is specified or
|
||||
// otherwise derives the set of supported base languages from tags.
|
||||
func (s *coverage) BaseLanguages() []Base {
|
||||
if s.bases == nil {
|
||||
tags := s.Tags()
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
a := make([]Base, len(tags))
|
||||
for i, t := range tags {
|
||||
a[i] = Base{language.Language(t.lang())}
|
||||
}
|
||||
sort.Sort(bases(a))
|
||||
k := 0
|
||||
for i := 1; i < len(a); i++ {
|
||||
if a[k] != a[i] {
|
||||
k++
|
||||
a[k] = a[i]
|
||||
}
|
||||
}
|
||||
return a[:k+1]
|
||||
}
|
||||
return s.bases()
|
||||
}
|
||||
|
||||
func (s *coverage) Scripts() []Script {
|
||||
if s.scripts == nil {
|
||||
return nil
|
||||
}
|
||||
return s.scripts()
|
||||
}
|
||||
|
||||
func (s *coverage) Regions() []Region {
|
||||
if s.regions == nil {
|
||||
return nil
|
||||
}
|
||||
return s.regions()
|
||||
}
|
||||
|
||||
// NewCoverage returns a Coverage for the given lists. It is typically used by
|
||||
// packages providing internationalization services to define their level of
|
||||
// coverage. A list may be of type []T or func() []T, where T is either Tag,
|
||||
// Base, Script or Region. The returned Coverage derives the value for Bases
|
||||
// from Tags if no func or slice for []Base is specified. For other unspecified
|
||||
// types the returned Coverage will return nil for the respective methods.
|
||||
func NewCoverage(list ...interface{}) Coverage {
|
||||
s := &coverage{}
|
||||
for _, x := range list {
|
||||
switch v := x.(type) {
|
||||
case func() []Base:
|
||||
s.bases = v
|
||||
case func() []Script:
|
||||
s.scripts = v
|
||||
case func() []Region:
|
||||
s.regions = v
|
||||
case func() []Tag:
|
||||
s.tags = v
|
||||
case []Base:
|
||||
s.bases = func() []Base { return v }
|
||||
case []Script:
|
||||
s.scripts = func() []Script { return v }
|
||||
case []Region:
|
||||
s.regions = func() []Region { return v }
|
||||
case []Tag:
|
||||
s.tags = func() []Tag { return v }
|
||||
default:
|
||||
panic(fmt.Sprintf("language: unsupported set type %T", v))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
102
vendor/golang.org/x/text/language/doc.go
generated
vendored
102
vendor/golang.org/x/text/language/doc.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package language implements BCP 47 language tags and related functionality.
|
||||
//
|
||||
// The most important function of package language is to match a list of
|
||||
// user-preferred languages to a list of supported languages.
|
||||
// It alleviates the developer of dealing with the complexity of this process
|
||||
// and provides the user with the best experience
|
||||
// (see https://blog.golang.org/matchlang).
|
||||
//
|
||||
//
|
||||
// Matching preferred against supported languages
|
||||
//
|
||||
// A Matcher for an application that supports English, Australian English,
|
||||
// Danish, and standard Mandarin can be created as follows:
|
||||
//
|
||||
// var matcher = language.NewMatcher([]language.Tag{
|
||||
// language.English, // The first language is used as fallback.
|
||||
// language.MustParse("en-AU"),
|
||||
// language.Danish,
|
||||
// language.Chinese,
|
||||
// })
|
||||
//
|
||||
// This list of supported languages is typically implied by the languages for
|
||||
// which there exists translations of the user interface.
|
||||
//
|
||||
// User-preferred languages usually come as a comma-separated list of BCP 47
|
||||
// language tags.
|
||||
// The MatchString finds best matches for such strings:
|
||||
//
|
||||
// handler(w http.ResponseWriter, r *http.Request) {
|
||||
// lang, _ := r.Cookie("lang")
|
||||
// accept := r.Header.Get("Accept-Language")
|
||||
// tag, _ := language.MatchStrings(matcher, lang.String(), accept)
|
||||
//
|
||||
// // tag should now be used for the initialization of any
|
||||
// // locale-specific service.
|
||||
// }
|
||||
//
|
||||
// The Matcher's Match method can be used to match Tags directly.
|
||||
//
|
||||
// Matchers are aware of the intricacies of equivalence between languages, such
|
||||
// as deprecated subtags, legacy tags, macro languages, mutual
|
||||
// intelligibility between scripts and languages, and transparently passing
|
||||
// BCP 47 user configuration.
|
||||
// For instance, it will know that a reader of Bokmål Danish can read Norwegian
|
||||
// and will know that Cantonese ("yue") is a good match for "zh-HK".
|
||||
//
|
||||
//
|
||||
// Using match results
|
||||
//
|
||||
// To guarantee a consistent user experience to the user it is important to
|
||||
// use the same language tag for the selection of any locale-specific services.
|
||||
// For example, it is utterly confusing to substitute spelled-out numbers
|
||||
// or dates in one language in text of another language.
|
||||
// More subtly confusing is using the wrong sorting order or casing
|
||||
// algorithm for a certain language.
|
||||
//
|
||||
// All the packages in x/text that provide locale-specific services
|
||||
// (e.g. collate, cases) should be initialized with the tag that was
|
||||
// obtained at the start of an interaction with the user.
|
||||
//
|
||||
// Note that Tag that is returned by Match and MatchString may differ from any
|
||||
// of the supported languages, as it may contain carried over settings from
|
||||
// the user tags.
|
||||
// This may be inconvenient when your application has some additional
|
||||
// locale-specific data for your supported languages.
|
||||
// Match and MatchString both return the index of the matched supported tag
|
||||
// to simplify associating such data with the matched tag.
|
||||
//
|
||||
//
|
||||
// Canonicalization
|
||||
//
|
||||
// If one uses the Matcher to compare languages one does not need to
|
||||
// worry about canonicalization.
|
||||
//
|
||||
// The meaning of a Tag varies per application. The language package
|
||||
// therefore delays canonicalization and preserves information as much
|
||||
// as possible. The Matcher, however, will always take into account that
|
||||
// two different tags may represent the same language.
|
||||
//
|
||||
// By default, only legacy and deprecated tags are converted into their
|
||||
// canonical equivalent. All other information is preserved. This approach makes
|
||||
// the confidence scores more accurate and allows matchers to distinguish
|
||||
// between variants that are otherwise lost.
|
||||
//
|
||||
// As a consequence, two tags that should be treated as identical according to
|
||||
// BCP 47 or CLDR, like "en-Latn" and "en", will be represented differently. The
|
||||
// Matcher handles such distinctions, though, and is aware of the
|
||||
// equivalence relations. The CanonType type can be used to alter the
|
||||
// canonicalization form.
|
||||
//
|
||||
// References
|
||||
//
|
||||
// BCP 47 - Tags for Identifying Languages http://tools.ietf.org/html/bcp47
|
||||
//
|
||||
package language // import "golang.org/x/text/language"
|
||||
|
||||
// TODO: explanation on how to match languages for your own locale-specific
|
||||
// service.
|
38
vendor/golang.org/x/text/language/go1_1.go
generated
vendored
38
vendor/golang.org/x/text/language/go1_1.go
generated
vendored
@ -1,38 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.2
|
||||
|
||||
package language
|
||||
|
||||
import "sort"
|
||||
|
||||
func sortStable(s sort.Interface) {
|
||||
ss := stableSort{
|
||||
s: s,
|
||||
pos: make([]int, s.Len()),
|
||||
}
|
||||
for i := range ss.pos {
|
||||
ss.pos[i] = i
|
||||
}
|
||||
sort.Sort(&ss)
|
||||
}
|
||||
|
||||
type stableSort struct {
|
||||
s sort.Interface
|
||||
pos []int
|
||||
}
|
||||
|
||||
func (s *stableSort) Len() int {
|
||||
return len(s.pos)
|
||||
}
|
||||
|
||||
func (s *stableSort) Less(i, j int) bool {
|
||||
return s.s.Less(i, j) || !s.s.Less(j, i) && s.pos[i] < s.pos[j]
|
||||
}
|
||||
|
||||
func (s *stableSort) Swap(i, j int) {
|
||||
s.s.Swap(i, j)
|
||||
s.pos[i], s.pos[j] = s.pos[j], s.pos[i]
|
||||
}
|
11
vendor/golang.org/x/text/language/go1_2.go
generated
vendored
11
vendor/golang.org/x/text/language/go1_2.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.2
|
||||
|
||||
package language
|
||||
|
||||
import "sort"
|
||||
|
||||
var sortStable = sort.Stable
|
601
vendor/golang.org/x/text/language/language.go
generated
vendored
601
vendor/golang.org/x/text/language/language.go
generated
vendored
@ -1,601 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go -output tables.go
|
||||
|
||||
package language
|
||||
|
||||
// TODO: Remove above NOTE after:
|
||||
// - verifying that tables are dropped correctly (most notably matcher tables).
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/internal/language"
|
||||
"golang.org/x/text/internal/language/compact"
|
||||
)
|
||||
|
||||
// Tag represents a BCP 47 language tag. It is used to specify an instance of a
|
||||
// specific language or locale. All language tag values are guaranteed to be
|
||||
// well-formed.
|
||||
type Tag compact.Tag
|
||||
|
||||
func makeTag(t language.Tag) (tag Tag) {
|
||||
return Tag(compact.Make(t))
|
||||
}
|
||||
|
||||
func (t *Tag) tag() language.Tag {
|
||||
return (*compact.Tag)(t).Tag()
|
||||
}
|
||||
|
||||
func (t *Tag) isCompact() bool {
|
||||
return (*compact.Tag)(t).IsCompact()
|
||||
}
|
||||
|
||||
// TODO: improve performance.
|
||||
func (t *Tag) lang() language.Language { return t.tag().LangID }
|
||||
func (t *Tag) region() language.Region { return t.tag().RegionID }
|
||||
func (t *Tag) script() language.Script { return t.tag().ScriptID }
|
||||
|
||||
// Make is a convenience wrapper for Parse that omits the error.
|
||||
// In case of an error, a sensible default is returned.
|
||||
func Make(s string) Tag {
|
||||
return Default.Make(s)
|
||||
}
|
||||
|
||||
// Make is a convenience wrapper for c.Parse that omits the error.
|
||||
// In case of an error, a sensible default is returned.
|
||||
func (c CanonType) Make(s string) Tag {
|
||||
t, _ := c.Parse(s)
|
||||
return t
|
||||
}
|
||||
|
||||
// Raw returns the raw base language, script and region, without making an
|
||||
// attempt to infer their values.
|
||||
func (t Tag) Raw() (b Base, s Script, r Region) {
|
||||
tt := t.tag()
|
||||
return Base{tt.LangID}, Script{tt.ScriptID}, Region{tt.RegionID}
|
||||
}
|
||||
|
||||
// IsRoot returns true if t is equal to language "und".
|
||||
func (t Tag) IsRoot() bool {
|
||||
return compact.Tag(t).IsRoot()
|
||||
}
|
||||
|
||||
// CanonType can be used to enable or disable various types of canonicalization.
|
||||
type CanonType int
|
||||
|
||||
const (
|
||||
// Replace deprecated base languages with their preferred replacements.
|
||||
DeprecatedBase CanonType = 1 << iota
|
||||
// Replace deprecated scripts with their preferred replacements.
|
||||
DeprecatedScript
|
||||
// Replace deprecated regions with their preferred replacements.
|
||||
DeprecatedRegion
|
||||
// Remove redundant scripts.
|
||||
SuppressScript
|
||||
// Normalize legacy encodings. This includes legacy languages defined in
|
||||
// CLDR as well as bibliographic codes defined in ISO-639.
|
||||
Legacy
|
||||
// Map the dominant language of a macro language group to the macro language
|
||||
// subtag. For example cmn -> zh.
|
||||
Macro
|
||||
// The CLDR flag should be used if full compatibility with CLDR is required.
|
||||
// There are a few cases where language.Tag may differ from CLDR. To follow all
|
||||
// of CLDR's suggestions, use All|CLDR.
|
||||
CLDR
|
||||
|
||||
// Raw can be used to Compose or Parse without Canonicalization.
|
||||
Raw CanonType = 0
|
||||
|
||||
// Replace all deprecated tags with their preferred replacements.
|
||||
Deprecated = DeprecatedBase | DeprecatedScript | DeprecatedRegion
|
||||
|
||||
// All canonicalizations recommended by BCP 47.
|
||||
BCP47 = Deprecated | SuppressScript
|
||||
|
||||
// All canonicalizations.
|
||||
All = BCP47 | Legacy | Macro
|
||||
|
||||
// Default is the canonicalization used by Parse, Make and Compose. To
|
||||
// preserve as much information as possible, canonicalizations that remove
|
||||
// potentially valuable information are not included. The Matcher is
|
||||
// designed to recognize similar tags that would be the same if
|
||||
// they were canonicalized using All.
|
||||
Default = Deprecated | Legacy
|
||||
|
||||
canonLang = DeprecatedBase | Legacy | Macro
|
||||
|
||||
// TODO: LikelyScript, LikelyRegion: suppress similar to ICU.
|
||||
)
|
||||
|
||||
// canonicalize returns the canonicalized equivalent of the tag and
|
||||
// whether there was any change.
|
||||
func canonicalize(c CanonType, t language.Tag) (language.Tag, bool) {
|
||||
if c == Raw {
|
||||
return t, false
|
||||
}
|
||||
changed := false
|
||||
if c&SuppressScript != 0 {
|
||||
if t.LangID.SuppressScript() == t.ScriptID {
|
||||
t.ScriptID = 0
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if c&canonLang != 0 {
|
||||
for {
|
||||
if l, aliasType := t.LangID.Canonicalize(); l != t.LangID {
|
||||
switch aliasType {
|
||||
case language.Legacy:
|
||||
if c&Legacy != 0 {
|
||||
if t.LangID == _sh && t.ScriptID == 0 {
|
||||
t.ScriptID = _Latn
|
||||
}
|
||||
t.LangID = l
|
||||
changed = true
|
||||
}
|
||||
case language.Macro:
|
||||
if c&Macro != 0 {
|
||||
// We deviate here from CLDR. The mapping "nb" -> "no"
|
||||
// qualifies as a typical Macro language mapping. However,
|
||||
// for legacy reasons, CLDR maps "no", the macro language
|
||||
// code for Norwegian, to the dominant variant "nb". This
|
||||
// change is currently under consideration for CLDR as well.
|
||||
// See https://unicode.org/cldr/trac/ticket/2698 and also
|
||||
// https://unicode.org/cldr/trac/ticket/1790 for some of the
|
||||
// practical implications. TODO: this check could be removed
|
||||
// if CLDR adopts this change.
|
||||
if c&CLDR == 0 || t.LangID != _nb {
|
||||
changed = true
|
||||
t.LangID = l
|
||||
}
|
||||
}
|
||||
case language.Deprecated:
|
||||
if c&DeprecatedBase != 0 {
|
||||
if t.LangID == _mo && t.RegionID == 0 {
|
||||
t.RegionID = _MD
|
||||
}
|
||||
t.LangID = l
|
||||
changed = true
|
||||
// Other canonicalization types may still apply.
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else if c&Legacy != 0 && t.LangID == _no && c&CLDR != 0 {
|
||||
t.LangID = _nb
|
||||
changed = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if c&DeprecatedScript != 0 {
|
||||
if t.ScriptID == _Qaai {
|
||||
changed = true
|
||||
t.ScriptID = _Zinh
|
||||
}
|
||||
}
|
||||
if c&DeprecatedRegion != 0 {
|
||||
if r := t.RegionID.Canonicalize(); r != t.RegionID {
|
||||
changed = true
|
||||
t.RegionID = r
|
||||
}
|
||||
}
|
||||
return t, changed
|
||||
}
|
||||
|
||||
// Canonicalize returns the canonicalized equivalent of the tag.
|
||||
func (c CanonType) Canonicalize(t Tag) (Tag, error) {
|
||||
// First try fast path.
|
||||
if t.isCompact() {
|
||||
if _, changed := canonicalize(c, compact.Tag(t).Tag()); !changed {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
// It is unlikely that one will canonicalize a tag after matching. So do
|
||||
// a slow but simple approach here.
|
||||
if tag, changed := canonicalize(c, t.tag()); changed {
|
||||
tag.RemakeString()
|
||||
return makeTag(tag), nil
|
||||
}
|
||||
return t, nil
|
||||
|
||||
}
|
||||
|
||||
// Confidence indicates the level of certainty for a given return value.
|
||||
// For example, Serbian may be written in Cyrillic or Latin script.
|
||||
// The confidence level indicates whether a value was explicitly specified,
|
||||
// whether it is typically the only possible value, or whether there is
|
||||
// an ambiguity.
|
||||
type Confidence int
|
||||
|
||||
const (
|
||||
No Confidence = iota // full confidence that there was no match
|
||||
Low // most likely value picked out of a set of alternatives
|
||||
High // value is generally assumed to be the correct match
|
||||
Exact // exact match or explicitly specified value
|
||||
)
|
||||
|
||||
var confName = []string{"No", "Low", "High", "Exact"}
|
||||
|
||||
func (c Confidence) String() string {
|
||||
return confName[c]
|
||||
}
|
||||
|
||||
// String returns the canonical string representation of the language tag.
|
||||
func (t Tag) String() string {
|
||||
return t.tag().String()
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (t Tag) MarshalText() (text []byte, err error) {
|
||||
return t.tag().MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (t *Tag) UnmarshalText(text []byte) error {
|
||||
var tag language.Tag
|
||||
err := tag.UnmarshalText(text)
|
||||
*t = makeTag(tag)
|
||||
return err
|
||||
}
|
||||
|
||||
// Base returns the base language of the language tag. If the base language is
|
||||
// unspecified, an attempt will be made to infer it from the context.
|
||||
// It uses a variant of CLDR's Add Likely Subtags algorithm. This is subject to change.
|
||||
func (t Tag) Base() (Base, Confidence) {
|
||||
if b := t.lang(); b != 0 {
|
||||
return Base{b}, Exact
|
||||
}
|
||||
tt := t.tag()
|
||||
c := High
|
||||
if tt.ScriptID == 0 && !tt.RegionID.IsCountry() {
|
||||
c = Low
|
||||
}
|
||||
if tag, err := tt.Maximize(); err == nil && tag.LangID != 0 {
|
||||
return Base{tag.LangID}, c
|
||||
}
|
||||
return Base{0}, No
|
||||
}
|
||||
|
||||
// Script infers the script for the language tag. If it was not explicitly given, it will infer
|
||||
// a most likely candidate.
|
||||
// If more than one script is commonly used for a language, the most likely one
|
||||
// is returned with a low confidence indication. For example, it returns (Cyrl, Low)
|
||||
// for Serbian.
|
||||
// If a script cannot be inferred (Zzzz, No) is returned. We do not use Zyyy (undetermined)
|
||||
// as one would suspect from the IANA registry for BCP 47. In a Unicode context Zyyy marks
|
||||
// common characters (like 1, 2, 3, '.', etc.) and is therefore more like multiple scripts.
|
||||
// See https://www.unicode.org/reports/tr24/#Values for more details. Zzzz is also used for
|
||||
// unknown value in CLDR. (Zzzz, Exact) is returned if Zzzz was explicitly specified.
|
||||
// Note that an inferred script is never guaranteed to be the correct one. Latin is
|
||||
// almost exclusively used for Afrikaans, but Arabic has been used for some texts
|
||||
// in the past. Also, the script that is commonly used may change over time.
|
||||
// It uses a variant of CLDR's Add Likely Subtags algorithm. This is subject to change.
|
||||
func (t Tag) Script() (Script, Confidence) {
|
||||
if scr := t.script(); scr != 0 {
|
||||
return Script{scr}, Exact
|
||||
}
|
||||
tt := t.tag()
|
||||
sc, c := language.Script(_Zzzz), No
|
||||
if scr := tt.LangID.SuppressScript(); scr != 0 {
|
||||
// Note: it is not always the case that a language with a suppress
|
||||
// script value is only written in one script (e.g. kk, ms, pa).
|
||||
if tt.RegionID == 0 {
|
||||
return Script{scr}, High
|
||||
}
|
||||
sc, c = scr, High
|
||||
}
|
||||
if tag, err := tt.Maximize(); err == nil {
|
||||
if tag.ScriptID != sc {
|
||||
sc, c = tag.ScriptID, Low
|
||||
}
|
||||
} else {
|
||||
tt, _ = canonicalize(Deprecated|Macro, tt)
|
||||
if tag, err := tt.Maximize(); err == nil && tag.ScriptID != sc {
|
||||
sc, c = tag.ScriptID, Low
|
||||
}
|
||||
}
|
||||
return Script{sc}, c
|
||||
}
|
||||
|
||||
// Region returns the region for the language tag. If it was not explicitly given, it will
|
||||
// infer a most likely candidate from the context.
|
||||
// It uses a variant of CLDR's Add Likely Subtags algorithm. This is subject to change.
|
||||
func (t Tag) Region() (Region, Confidence) {
|
||||
if r := t.region(); r != 0 {
|
||||
return Region{r}, Exact
|
||||
}
|
||||
tt := t.tag()
|
||||
if tt, err := tt.Maximize(); err == nil {
|
||||
return Region{tt.RegionID}, Low // TODO: differentiate between high and low.
|
||||
}
|
||||
tt, _ = canonicalize(Deprecated|Macro, tt)
|
||||
if tag, err := tt.Maximize(); err == nil {
|
||||
return Region{tag.RegionID}, Low
|
||||
}
|
||||
return Region{_ZZ}, No // TODO: return world instead of undetermined?
|
||||
}
|
||||
|
||||
// Variants returns the variants specified explicitly for this language tag.
|
||||
// or nil if no variant was specified.
|
||||
func (t Tag) Variants() []Variant {
|
||||
if !compact.Tag(t).MayHaveVariants() {
|
||||
return nil
|
||||
}
|
||||
v := []Variant{}
|
||||
x, str := "", t.tag().Variants()
|
||||
for str != "" {
|
||||
x, str = nextToken(str)
|
||||
v = append(v, Variant{x})
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Parent returns the CLDR parent of t. In CLDR, missing fields in data for a
|
||||
// specific language are substituted with fields from the parent language.
|
||||
// The parent for a language may change for newer versions of CLDR.
|
||||
//
|
||||
// Parent returns a tag for a less specific language that is mutually
|
||||
// intelligible or Und if there is no such language. This may not be the same as
|
||||
// simply stripping the last BCP 47 subtag. For instance, the parent of "zh-TW"
|
||||
// is "zh-Hant", and the parent of "zh-Hant" is "und".
|
||||
func (t Tag) Parent() Tag {
|
||||
return Tag(compact.Tag(t).Parent())
|
||||
}
|
||||
|
||||
// returns token t and the rest of the string.
|
||||
func nextToken(s string) (t, tail string) {
|
||||
p := strings.Index(s[1:], "-")
|
||||
if p == -1 {
|
||||
return s[1:], ""
|
||||
}
|
||||
p++
|
||||
return s[1:p], s[p:]
|
||||
}
|
||||
|
||||
// Extension is a single BCP 47 extension.
|
||||
type Extension struct {
|
||||
s string
|
||||
}
|
||||
|
||||
// String returns the string representation of the extension, including the
|
||||
// type tag.
|
||||
func (e Extension) String() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
// ParseExtension parses s as an extension and returns it on success.
|
||||
func ParseExtension(s string) (e Extension, err error) {
|
||||
ext, err := language.ParseExtension(s)
|
||||
return Extension{ext}, err
|
||||
}
|
||||
|
||||
// Type returns the one-byte extension type of e. It returns 0 for the zero
|
||||
// exception.
|
||||
func (e Extension) Type() byte {
|
||||
if e.s == "" {
|
||||
return 0
|
||||
}
|
||||
return e.s[0]
|
||||
}
|
||||
|
||||
// Tokens returns the list of tokens of e.
|
||||
func (e Extension) Tokens() []string {
|
||||
return strings.Split(e.s, "-")
|
||||
}
|
||||
|
||||
// Extension returns the extension of type x for tag t. It will return
|
||||
// false for ok if t does not have the requested extension. The returned
|
||||
// extension will be invalid in this case.
|
||||
func (t Tag) Extension(x byte) (ext Extension, ok bool) {
|
||||
if !compact.Tag(t).MayHaveExtensions() {
|
||||
return Extension{}, false
|
||||
}
|
||||
e, ok := t.tag().Extension(x)
|
||||
return Extension{e}, ok
|
||||
}
|
||||
|
||||
// Extensions returns all extensions of t.
|
||||
func (t Tag) Extensions() []Extension {
|
||||
if !compact.Tag(t).MayHaveExtensions() {
|
||||
return nil
|
||||
}
|
||||
e := []Extension{}
|
||||
for _, ext := range t.tag().Extensions() {
|
||||
e = append(e, Extension{ext})
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// TypeForKey returns the type associated with the given key, where key and type
|
||||
// are of the allowed values defined for the Unicode locale extension ('u') in
|
||||
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
|
||||
// TypeForKey will traverse the inheritance chain to get the correct value.
|
||||
func (t Tag) TypeForKey(key string) string {
|
||||
if !compact.Tag(t).MayHaveExtensions() {
|
||||
if key != "rg" && key != "va" {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return t.tag().TypeForKey(key)
|
||||
}
|
||||
|
||||
// SetTypeForKey returns a new Tag with the key set to type, where key and type
|
||||
// are of the allowed values defined for the Unicode locale extension ('u') in
|
||||
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
|
||||
// An empty value removes an existing pair with the same key.
|
||||
func (t Tag) SetTypeForKey(key, value string) (Tag, error) {
|
||||
tt, err := t.tag().SetTypeForKey(key, value)
|
||||
return makeTag(tt), err
|
||||
}
|
||||
|
||||
// NumCompactTags is the number of compact tags. The maximum tag is
|
||||
// NumCompactTags-1.
|
||||
const NumCompactTags = compact.NumCompactTags
|
||||
|
||||
// CompactIndex returns an index, where 0 <= index < NumCompactTags, for tags
|
||||
// for which data exists in the text repository.The index will change over time
|
||||
// and should not be stored in persistent storage. If t does not match a compact
|
||||
// index, exact will be false and the compact index will be returned for the
|
||||
// first match after repeatedly taking the Parent of t.
|
||||
func CompactIndex(t Tag) (index int, exact bool) {
|
||||
id, exact := compact.LanguageID(compact.Tag(t))
|
||||
return int(id), exact
|
||||
}
|
||||
|
||||
var root = language.Tag{}
|
||||
|
||||
// Base is an ISO 639 language code, used for encoding the base language
|
||||
// of a language tag.
|
||||
type Base struct {
|
||||
langID language.Language
|
||||
}
|
||||
|
||||
// ParseBase parses a 2- or 3-letter ISO 639 code.
|
||||
// It returns a ValueError if s is a well-formed but unknown language identifier
|
||||
// or another error if another error occurred.
|
||||
func ParseBase(s string) (Base, error) {
|
||||
l, err := language.ParseBase(s)
|
||||
return Base{l}, err
|
||||
}
|
||||
|
||||
// String returns the BCP 47 representation of the base language.
|
||||
func (b Base) String() string {
|
||||
return b.langID.String()
|
||||
}
|
||||
|
||||
// ISO3 returns the ISO 639-3 language code.
|
||||
func (b Base) ISO3() string {
|
||||
return b.langID.ISO3()
|
||||
}
|
||||
|
||||
// IsPrivateUse reports whether this language code is reserved for private use.
|
||||
func (b Base) IsPrivateUse() bool {
|
||||
return b.langID.IsPrivateUse()
|
||||
}
|
||||
|
||||
// Script is a 4-letter ISO 15924 code for representing scripts.
|
||||
// It is idiomatically represented in title case.
|
||||
type Script struct {
|
||||
scriptID language.Script
|
||||
}
|
||||
|
||||
// ParseScript parses a 4-letter ISO 15924 code.
|
||||
// It returns a ValueError if s is a well-formed but unknown script identifier
|
||||
// or another error if another error occurred.
|
||||
func ParseScript(s string) (Script, error) {
|
||||
sc, err := language.ParseScript(s)
|
||||
return Script{sc}, err
|
||||
}
|
||||
|
||||
// String returns the script code in title case.
|
||||
// It returns "Zzzz" for an unspecified script.
|
||||
func (s Script) String() string {
|
||||
return s.scriptID.String()
|
||||
}
|
||||
|
||||
// IsPrivateUse reports whether this script code is reserved for private use.
|
||||
func (s Script) IsPrivateUse() bool {
|
||||
return s.scriptID.IsPrivateUse()
|
||||
}
|
||||
|
||||
// Region is an ISO 3166-1 or UN M.49 code for representing countries and regions.
|
||||
type Region struct {
|
||||
regionID language.Region
|
||||
}
|
||||
|
||||
// EncodeM49 returns the Region for the given UN M.49 code.
|
||||
// It returns an error if r is not a valid code.
|
||||
func EncodeM49(r int) (Region, error) {
|
||||
rid, err := language.EncodeM49(r)
|
||||
return Region{rid}, err
|
||||
}
|
||||
|
||||
// ParseRegion parses a 2- or 3-letter ISO 3166-1 or a UN M.49 code.
|
||||
// It returns a ValueError if s is a well-formed but unknown region identifier
|
||||
// or another error if another error occurred.
|
||||
func ParseRegion(s string) (Region, error) {
|
||||
r, err := language.ParseRegion(s)
|
||||
return Region{r}, err
|
||||
}
|
||||
|
||||
// String returns the BCP 47 representation for the region.
|
||||
// It returns "ZZ" for an unspecified region.
|
||||
func (r Region) String() string {
|
||||
return r.regionID.String()
|
||||
}
|
||||
|
||||
// ISO3 returns the 3-letter ISO code of r.
|
||||
// Note that not all regions have a 3-letter ISO code.
|
||||
// In such cases this method returns "ZZZ".
|
||||
func (r Region) ISO3() string {
|
||||
return r.regionID.ISO3()
|
||||
}
|
||||
|
||||
// M49 returns the UN M.49 encoding of r, or 0 if this encoding
|
||||
// is not defined for r.
|
||||
func (r Region) M49() int {
|
||||
return r.regionID.M49()
|
||||
}
|
||||
|
||||
// IsPrivateUse reports whether r has the ISO 3166 User-assigned status. This
|
||||
// may include private-use tags that are assigned by CLDR and used in this
|
||||
// implementation. So IsPrivateUse and IsCountry can be simultaneously true.
|
||||
func (r Region) IsPrivateUse() bool {
|
||||
return r.regionID.IsPrivateUse()
|
||||
}
|
||||
|
||||
// IsCountry returns whether this region is a country or autonomous area. This
|
||||
// includes non-standard definitions from CLDR.
|
||||
func (r Region) IsCountry() bool {
|
||||
return r.regionID.IsCountry()
|
||||
}
|
||||
|
||||
// IsGroup returns whether this region defines a collection of regions. This
|
||||
// includes non-standard definitions from CLDR.
|
||||
func (r Region) IsGroup() bool {
|
||||
return r.regionID.IsGroup()
|
||||
}
|
||||
|
||||
// Contains returns whether Region c is contained by Region r. It returns true
|
||||
// if c == r.
|
||||
func (r Region) Contains(c Region) bool {
|
||||
return r.regionID.Contains(c.regionID)
|
||||
}
|
||||
|
||||
// TLD returns the country code top-level domain (ccTLD). UK is returned for GB.
|
||||
// In all other cases it returns either the region itself or an error.
|
||||
//
|
||||
// This method may return an error for a region for which there exists a
|
||||
// canonical form with a ccTLD. To get that ccTLD canonicalize r first. The
|
||||
// region will already be canonicalized it was obtained from a Tag that was
|
||||
// obtained using any of the default methods.
|
||||
func (r Region) TLD() (Region, error) {
|
||||
tld, err := r.regionID.TLD()
|
||||
return Region{tld}, err
|
||||
}
|
||||
|
||||
// Canonicalize returns the region or a possible replacement if the region is
|
||||
// deprecated. It will not return a replacement for deprecated regions that
|
||||
// are split into multiple regions.
|
||||
func (r Region) Canonicalize() Region {
|
||||
return Region{r.regionID.Canonicalize()}
|
||||
}
|
||||
|
||||
// Variant represents a registered variant of a language as defined by BCP 47.
|
||||
type Variant struct {
|
||||
variant string
|
||||
}
|
||||
|
||||
// ParseVariant parses and returns a Variant. An error is returned if s is not
|
||||
// a valid variant.
|
||||
func ParseVariant(s string) (Variant, error) {
|
||||
v, err := language.ParseVariant(s)
|
||||
return Variant{v.String()}, err
|
||||
}
|
||||
|
||||
// String returns the string representation of the variant.
|
||||
func (v Variant) String() string {
|
||||
return v.variant
|
||||
}
|
735
vendor/golang.org/x/text/language/match.go
generated
vendored
735
vendor/golang.org/x/text/language/match.go
generated
vendored
@ -1,735 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package language
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/internal/language"
|
||||
)
|
||||
|
||||
// A MatchOption configures a Matcher.
|
||||
type MatchOption func(*matcher)
|
||||
|
||||
// PreferSameScript will, in the absence of a match, result in the first
|
||||
// preferred tag with the same script as a supported tag to match this supported
|
||||
// tag. The default is currently true, but this may change in the future.
|
||||
func PreferSameScript(preferSame bool) MatchOption {
|
||||
return func(m *matcher) { m.preferSameScript = preferSame }
|
||||
}
|
||||
|
||||
// TODO(v1.0.0): consider making Matcher a concrete type, instead of interface.
|
||||
// There doesn't seem to be too much need for multiple types.
|
||||
// Making it a concrete type allows MatchStrings to be a method, which will
|
||||
// improve its discoverability.
|
||||
|
||||
// MatchStrings parses and matches the given strings until one of them matches
|
||||
// the language in the Matcher. A string may be an Accept-Language header as
|
||||
// handled by ParseAcceptLanguage. The default language is returned if no
|
||||
// other language matched.
|
||||
func MatchStrings(m Matcher, lang ...string) (tag Tag, index int) {
|
||||
for _, accept := range lang {
|
||||
desired, _, err := ParseAcceptLanguage(accept)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if tag, index, conf := m.Match(desired...); conf != No {
|
||||
return tag, index
|
||||
}
|
||||
}
|
||||
tag, index, _ = m.Match()
|
||||
return
|
||||
}
|
||||
|
||||
// Matcher is the interface that wraps the Match method.
|
||||
//
|
||||
// Match returns the best match for any of the given tags, along with
|
||||
// a unique index associated with the returned tag and a confidence
|
||||
// score.
|
||||
type Matcher interface {
|
||||
Match(t ...Tag) (tag Tag, index int, c Confidence)
|
||||
}
|
||||
|
||||
// Comprehends reports the confidence score for a speaker of a given language
|
||||
// to being able to comprehend the written form of an alternative language.
|
||||
func Comprehends(speaker, alternative Tag) Confidence {
|
||||
_, _, c := NewMatcher([]Tag{alternative}).Match(speaker)
|
||||
return c
|
||||
}
|
||||
|
||||
// NewMatcher returns a Matcher that matches an ordered list of preferred tags
|
||||
// against a list of supported tags based on written intelligibility, closeness
|
||||
// of dialect, equivalence of subtags and various other rules. It is initialized
|
||||
// with the list of supported tags. The first element is used as the default
|
||||
// value in case no match is found.
|
||||
//
|
||||
// Its Match method matches the first of the given Tags to reach a certain
|
||||
// confidence threshold. The tags passed to Match should therefore be specified
|
||||
// in order of preference. Extensions are ignored for matching.
|
||||
//
|
||||
// The index returned by the Match method corresponds to the index of the
|
||||
// matched tag in t, but is augmented with the Unicode extension ('u')of the
|
||||
// corresponding preferred tag. This allows user locale options to be passed
|
||||
// transparently.
|
||||
func NewMatcher(t []Tag, options ...MatchOption) Matcher {
|
||||
return newMatcher(t, options)
|
||||
}
|
||||
|
||||
func (m *matcher) Match(want ...Tag) (t Tag, index int, c Confidence) {
|
||||
var tt language.Tag
|
||||
match, w, c := m.getBest(want...)
|
||||
if match != nil {
|
||||
tt, index = match.tag, match.index
|
||||
} else {
|
||||
// TODO: this should be an option
|
||||
tt = m.default_.tag
|
||||
if m.preferSameScript {
|
||||
outer:
|
||||
for _, w := range want {
|
||||
script, _ := w.Script()
|
||||
if script.scriptID == 0 {
|
||||
// Don't do anything if there is no script, such as with
|
||||
// private subtags.
|
||||
continue
|
||||
}
|
||||
for i, h := range m.supported {
|
||||
if script.scriptID == h.maxScript {
|
||||
tt, index = h.tag, i
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: select first language tag based on script.
|
||||
}
|
||||
if w.RegionID != tt.RegionID && w.RegionID != 0 {
|
||||
if w.RegionID != 0 && tt.RegionID != 0 && tt.RegionID.Contains(w.RegionID) {
|
||||
tt.RegionID = w.RegionID
|
||||
tt.RemakeString()
|
||||
} else if r := w.RegionID.String(); len(r) == 2 {
|
||||
// TODO: also filter macro and deprecated.
|
||||
tt, _ = tt.SetTypeForKey("rg", strings.ToLower(r)+"zzzz")
|
||||
}
|
||||
}
|
||||
// Copy options from the user-provided tag into the result tag. This is hard
|
||||
// to do after the fact, so we do it here.
|
||||
// TODO: add in alternative variants to -u-va-.
|
||||
// TODO: add preferred region to -u-rg-.
|
||||
if e := w.Extensions(); len(e) > 0 {
|
||||
b := language.Builder{}
|
||||
b.SetTag(tt)
|
||||
for _, e := range e {
|
||||
b.AddExt(e)
|
||||
}
|
||||
tt = b.Make()
|
||||
}
|
||||
return makeTag(tt), index, c
|
||||
}
|
||||
|
||||
// ErrMissingLikelyTagsData indicates no information was available
|
||||
// to compute likely values of missing tags.
|
||||
var ErrMissingLikelyTagsData = errors.New("missing likely tags data")
|
||||
|
||||
// func (t *Tag) setTagsFrom(id Tag) {
|
||||
// t.LangID = id.LangID
|
||||
// t.ScriptID = id.ScriptID
|
||||
// t.RegionID = id.RegionID
|
||||
// }
|
||||
|
||||
// Tag Matching
|
||||
// CLDR defines an algorithm for finding the best match between two sets of language
|
||||
// tags. The basic algorithm defines how to score a possible match and then find
|
||||
// the match with the best score
|
||||
// (see https://www.unicode.org/reports/tr35/#LanguageMatching).
|
||||
// Using scoring has several disadvantages. The scoring obfuscates the importance of
|
||||
// the various factors considered, making the algorithm harder to understand. Using
|
||||
// scoring also requires the full score to be computed for each pair of tags.
|
||||
//
|
||||
// We will use a different algorithm which aims to have the following properties:
|
||||
// - clarity on the precedence of the various selection factors, and
|
||||
// - improved performance by allowing early termination of a comparison.
|
||||
//
|
||||
// Matching algorithm (overview)
|
||||
// Input:
|
||||
// - supported: a set of supported tags
|
||||
// - default: the default tag to return in case there is no match
|
||||
// - desired: list of desired tags, ordered by preference, starting with
|
||||
// the most-preferred.
|
||||
//
|
||||
// Algorithm:
|
||||
// 1) Set the best match to the lowest confidence level
|
||||
// 2) For each tag in "desired":
|
||||
// a) For each tag in "supported":
|
||||
// 1) compute the match between the two tags.
|
||||
// 2) if the match is better than the previous best match, replace it
|
||||
// with the new match. (see next section)
|
||||
// b) if the current best match is Exact and pin is true the result will be
|
||||
// frozen to the language found thusfar, although better matches may
|
||||
// still be found for the same language.
|
||||
// 3) If the best match so far is below a certain threshold, return "default".
|
||||
//
|
||||
// Ranking:
|
||||
// We use two phases to determine whether one pair of tags are a better match
|
||||
// than another pair of tags. First, we determine a rough confidence level. If the
|
||||
// levels are different, the one with the highest confidence wins.
|
||||
// Second, if the rough confidence levels are identical, we use a set of tie-breaker
|
||||
// rules.
|
||||
//
|
||||
// The confidence level of matching a pair of tags is determined by finding the
|
||||
// lowest confidence level of any matches of the corresponding subtags (the
|
||||
// result is deemed as good as its weakest link).
|
||||
// We define the following levels:
|
||||
// Exact - An exact match of a subtag, before adding likely subtags.
|
||||
// MaxExact - An exact match of a subtag, after adding likely subtags.
|
||||
// [See Note 2].
|
||||
// High - High level of mutual intelligibility between different subtag
|
||||
// variants.
|
||||
// Low - Low level of mutual intelligibility between different subtag
|
||||
// variants.
|
||||
// No - No mutual intelligibility.
|
||||
//
|
||||
// The following levels can occur for each type of subtag:
|
||||
// Base: Exact, MaxExact, High, Low, No
|
||||
// Script: Exact, MaxExact [see Note 3], Low, No
|
||||
// Region: Exact, MaxExact, High
|
||||
// Variant: Exact, High
|
||||
// Private: Exact, No
|
||||
//
|
||||
// Any result with a confidence level of Low or higher is deemed a possible match.
|
||||
// Once a desired tag matches any of the supported tags with a level of MaxExact
|
||||
// or higher, the next desired tag is not considered (see Step 2.b).
|
||||
// Note that CLDR provides languageMatching data that defines close equivalence
|
||||
// classes for base languages, scripts and regions.
|
||||
//
|
||||
// Tie-breaking
|
||||
// If we get the same confidence level for two matches, we apply a sequence of
|
||||
// tie-breaking rules. The first that succeeds defines the result. The rules are
|
||||
// applied in the following order.
|
||||
// 1) Original language was defined and was identical.
|
||||
// 2) Original region was defined and was identical.
|
||||
// 3) Distance between two maximized regions was the smallest.
|
||||
// 4) Original script was defined and was identical.
|
||||
// 5) Distance from want tag to have tag using the parent relation [see Note 5.]
|
||||
// If there is still no winner after these rules are applied, the first match
|
||||
// found wins.
|
||||
//
|
||||
// Notes:
|
||||
// [2] In practice, as matching of Exact is done in a separate phase from
|
||||
// matching the other levels, we reuse the Exact level to mean MaxExact in
|
||||
// the second phase. As a consequence, we only need the levels defined by
|
||||
// the Confidence type. The MaxExact confidence level is mapped to High in
|
||||
// the public API.
|
||||
// [3] We do not differentiate between maximized script values that were derived
|
||||
// from suppressScript versus most likely tag data. We determined that in
|
||||
// ranking the two, one ranks just after the other. Moreover, the two cannot
|
||||
// occur concurrently. As a consequence, they are identical for practical
|
||||
// purposes.
|
||||
// [4] In case of deprecated, macro-equivalents and legacy mappings, we assign
|
||||
// the MaxExact level to allow iw vs he to still be a closer match than
|
||||
// en-AU vs en-US, for example.
|
||||
// [5] In CLDR a locale inherits fields that are unspecified for this locale
|
||||
// from its parent. Therefore, if a locale is a parent of another locale,
|
||||
// it is a strong measure for closeness, especially when no other tie
|
||||
// breaker rule applies. One could also argue it is inconsistent, for
|
||||
// example, when pt-AO matches pt (which CLDR equates with pt-BR), even
|
||||
// though its parent is pt-PT according to the inheritance rules.
|
||||
//
|
||||
// Implementation Details:
|
||||
// There are several performance considerations worth pointing out. Most notably,
|
||||
// we preprocess as much as possible (within reason) at the time of creation of a
|
||||
// matcher. This includes:
|
||||
// - creating a per-language map, which includes data for the raw base language
|
||||
// and its canonicalized variant (if applicable),
|
||||
// - expanding entries for the equivalence classes defined in CLDR's
|
||||
// languageMatch data.
|
||||
// The per-language map ensures that typically only a very small number of tags
|
||||
// need to be considered. The pre-expansion of canonicalized subtags and
|
||||
// equivalence classes reduces the amount of map lookups that need to be done at
|
||||
// runtime.
|
||||
|
||||
// matcher keeps a set of supported language tags, indexed by language.
|
||||
type matcher struct {
|
||||
default_ *haveTag
|
||||
supported []*haveTag
|
||||
index map[language.Language]*matchHeader
|
||||
passSettings bool
|
||||
preferSameScript bool
|
||||
}
|
||||
|
||||
// matchHeader has the lists of tags for exact matches and matches based on
|
||||
// maximized and canonicalized tags for a given language.
|
||||
type matchHeader struct {
|
||||
haveTags []*haveTag
|
||||
original bool
|
||||
}
|
||||
|
||||
// haveTag holds a supported Tag and its maximized script and region. The maximized
|
||||
// or canonicalized language is not stored as it is not needed during matching.
|
||||
type haveTag struct {
|
||||
tag language.Tag
|
||||
|
||||
// index of this tag in the original list of supported tags.
|
||||
index int
|
||||
|
||||
// conf is the maximum confidence that can result from matching this haveTag.
|
||||
// When conf < Exact this means it was inserted after applying a CLDR equivalence rule.
|
||||
conf Confidence
|
||||
|
||||
// Maximized region and script.
|
||||
maxRegion language.Region
|
||||
maxScript language.Script
|
||||
|
||||
// altScript may be checked as an alternative match to maxScript. If altScript
|
||||
// matches, the confidence level for this match is Low. Theoretically there
|
||||
// could be multiple alternative scripts. This does not occur in practice.
|
||||
altScript language.Script
|
||||
|
||||
// nextMax is the index of the next haveTag with the same maximized tags.
|
||||
nextMax uint16
|
||||
}
|
||||
|
||||
func makeHaveTag(tag language.Tag, index int) (haveTag, language.Language) {
|
||||
max := tag
|
||||
if tag.LangID != 0 || tag.RegionID != 0 || tag.ScriptID != 0 {
|
||||
max, _ = canonicalize(All, max)
|
||||
max, _ = max.Maximize()
|
||||
max.RemakeString()
|
||||
}
|
||||
return haveTag{tag, index, Exact, max.RegionID, max.ScriptID, altScript(max.LangID, max.ScriptID), 0}, max.LangID
|
||||
}
|
||||
|
||||
// altScript returns an alternative script that may match the given script with
|
||||
// a low confidence. At the moment, the langMatch data allows for at most one
|
||||
// script to map to another and we rely on this to keep the code simple.
|
||||
func altScript(l language.Language, s language.Script) language.Script {
|
||||
for _, alt := range matchScript {
|
||||
// TODO: also match cases where language is not the same.
|
||||
if (language.Language(alt.wantLang) == l || language.Language(alt.haveLang) == l) &&
|
||||
language.Script(alt.haveScript) == s {
|
||||
return language.Script(alt.wantScript)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// addIfNew adds a haveTag to the list of tags only if it is a unique tag.
|
||||
// Tags that have the same maximized values are linked by index.
|
||||
func (h *matchHeader) addIfNew(n haveTag, exact bool) {
|
||||
h.original = h.original || exact
|
||||
// Don't add new exact matches.
|
||||
for _, v := range h.haveTags {
|
||||
if equalsRest(v.tag, n.tag) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Allow duplicate maximized tags, but create a linked list to allow quickly
|
||||
// comparing the equivalents and bail out.
|
||||
for i, v := range h.haveTags {
|
||||
if v.maxScript == n.maxScript &&
|
||||
v.maxRegion == n.maxRegion &&
|
||||
v.tag.VariantOrPrivateUseTags() == n.tag.VariantOrPrivateUseTags() {
|
||||
for h.haveTags[i].nextMax != 0 {
|
||||
i = int(h.haveTags[i].nextMax)
|
||||
}
|
||||
h.haveTags[i].nextMax = uint16(len(h.haveTags))
|
||||
break
|
||||
}
|
||||
}
|
||||
h.haveTags = append(h.haveTags, &n)
|
||||
}
|
||||
|
||||
// header returns the matchHeader for the given language. It creates one if
|
||||
// it doesn't already exist.
|
||||
func (m *matcher) header(l language.Language) *matchHeader {
|
||||
if h := m.index[l]; h != nil {
|
||||
return h
|
||||
}
|
||||
h := &matchHeader{}
|
||||
m.index[l] = h
|
||||
return h
|
||||
}
|
||||
|
||||
func toConf(d uint8) Confidence {
|
||||
if d <= 10 {
|
||||
return High
|
||||
}
|
||||
if d < 30 {
|
||||
return Low
|
||||
}
|
||||
return No
|
||||
}
|
||||
|
||||
// newMatcher builds an index for the given supported tags and returns it as
|
||||
// a matcher. It also expands the index by considering various equivalence classes
|
||||
// for a given tag.
|
||||
func newMatcher(supported []Tag, options []MatchOption) *matcher {
|
||||
m := &matcher{
|
||||
index: make(map[language.Language]*matchHeader),
|
||||
preferSameScript: true,
|
||||
}
|
||||
for _, o := range options {
|
||||
o(m)
|
||||
}
|
||||
if len(supported) == 0 {
|
||||
m.default_ = &haveTag{}
|
||||
return m
|
||||
}
|
||||
// Add supported languages to the index. Add exact matches first to give
|
||||
// them precedence.
|
||||
for i, tag := range supported {
|
||||
tt := tag.tag()
|
||||
pair, _ := makeHaveTag(tt, i)
|
||||
m.header(tt.LangID).addIfNew(pair, true)
|
||||
m.supported = append(m.supported, &pair)
|
||||
}
|
||||
m.default_ = m.header(supported[0].lang()).haveTags[0]
|
||||
// Keep these in two different loops to support the case that two equivalent
|
||||
// languages are distinguished, such as iw and he.
|
||||
for i, tag := range supported {
|
||||
tt := tag.tag()
|
||||
pair, max := makeHaveTag(tt, i)
|
||||
if max != tt.LangID {
|
||||
m.header(max).addIfNew(pair, true)
|
||||
}
|
||||
}
|
||||
|
||||
// update is used to add indexes in the map for equivalent languages.
|
||||
// update will only add entries to original indexes, thus not computing any
|
||||
// transitive relations.
|
||||
update := func(want, have uint16, conf Confidence) {
|
||||
if hh := m.index[language.Language(have)]; hh != nil {
|
||||
if !hh.original {
|
||||
return
|
||||
}
|
||||
hw := m.header(language.Language(want))
|
||||
for _, ht := range hh.haveTags {
|
||||
v := *ht
|
||||
if conf < v.conf {
|
||||
v.conf = conf
|
||||
}
|
||||
v.nextMax = 0 // this value needs to be recomputed
|
||||
if v.altScript != 0 {
|
||||
v.altScript = altScript(language.Language(want), v.maxScript)
|
||||
}
|
||||
hw.addIfNew(v, conf == Exact && hh.original)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add entries for languages with mutual intelligibility as defined by CLDR's
|
||||
// languageMatch data.
|
||||
for _, ml := range matchLang {
|
||||
update(ml.want, ml.have, toConf(ml.distance))
|
||||
if !ml.oneway {
|
||||
update(ml.have, ml.want, toConf(ml.distance))
|
||||
}
|
||||
}
|
||||
|
||||
// Add entries for possible canonicalizations. This is an optimization to
|
||||
// ensure that only one map lookup needs to be done at runtime per desired tag.
|
||||
// First we match deprecated equivalents. If they are perfect equivalents
|
||||
// (their canonicalization simply substitutes a different language code, but
|
||||
// nothing else), the match confidence is Exact, otherwise it is High.
|
||||
for i, lm := range language.AliasMap {
|
||||
// If deprecated codes match and there is no fiddling with the script or
|
||||
// or region, we consider it an exact match.
|
||||
conf := Exact
|
||||
if language.AliasTypes[i] != language.Macro {
|
||||
if !isExactEquivalent(language.Language(lm.From)) {
|
||||
conf = High
|
||||
}
|
||||
update(lm.To, lm.From, conf)
|
||||
}
|
||||
update(lm.From, lm.To, conf)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// getBest gets the best matching tag in m for any of the given tags, taking into
|
||||
// account the order of preference of the given tags.
|
||||
func (m *matcher) getBest(want ...Tag) (got *haveTag, orig language.Tag, c Confidence) {
|
||||
best := bestMatch{}
|
||||
for i, ww := range want {
|
||||
w := ww.tag()
|
||||
var max language.Tag
|
||||
// Check for exact match first.
|
||||
h := m.index[w.LangID]
|
||||
if w.LangID != 0 {
|
||||
if h == nil {
|
||||
continue
|
||||
}
|
||||
// Base language is defined.
|
||||
max, _ = canonicalize(Legacy|Deprecated|Macro, w)
|
||||
// A region that is added through canonicalization is stronger than
|
||||
// a maximized region: set it in the original (e.g. mo -> ro-MD).
|
||||
if w.RegionID != max.RegionID {
|
||||
w.RegionID = max.RegionID
|
||||
}
|
||||
// TODO: should we do the same for scripts?
|
||||
// See test case: en, sr, nl ; sh ; sr
|
||||
max, _ = max.Maximize()
|
||||
} else {
|
||||
// Base language is not defined.
|
||||
if h != nil {
|
||||
for i := range h.haveTags {
|
||||
have := h.haveTags[i]
|
||||
if equalsRest(have.tag, w) {
|
||||
return have, w, Exact
|
||||
}
|
||||
}
|
||||
}
|
||||
if w.ScriptID == 0 && w.RegionID == 0 {
|
||||
// We skip all tags matching und for approximate matching, including
|
||||
// private tags.
|
||||
continue
|
||||
}
|
||||
max, _ = w.Maximize()
|
||||
if h = m.index[max.LangID]; h == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
pin := true
|
||||
for _, t := range want[i+1:] {
|
||||
if w.LangID == t.lang() {
|
||||
pin = false
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check for match based on maximized tag.
|
||||
for i := range h.haveTags {
|
||||
have := h.haveTags[i]
|
||||
best.update(have, w, max.ScriptID, max.RegionID, pin)
|
||||
if best.conf == Exact {
|
||||
for have.nextMax != 0 {
|
||||
have = h.haveTags[have.nextMax]
|
||||
best.update(have, w, max.ScriptID, max.RegionID, pin)
|
||||
}
|
||||
return best.have, best.want, best.conf
|
||||
}
|
||||
}
|
||||
}
|
||||
if best.conf <= No {
|
||||
if len(want) != 0 {
|
||||
return nil, want[0].tag(), No
|
||||
}
|
||||
return nil, language.Tag{}, No
|
||||
}
|
||||
return best.have, best.want, best.conf
|
||||
}
|
||||
|
||||
// bestMatch accumulates the best match so far.
|
||||
type bestMatch struct {
|
||||
have *haveTag
|
||||
want language.Tag
|
||||
conf Confidence
|
||||
pinnedRegion language.Region
|
||||
pinLanguage bool
|
||||
sameRegionGroup bool
|
||||
// Cached results from applying tie-breaking rules.
|
||||
origLang bool
|
||||
origReg bool
|
||||
paradigmReg bool
|
||||
regGroupDist uint8
|
||||
origScript bool
|
||||
}
|
||||
|
||||
// update updates the existing best match if the new pair is considered to be a
|
||||
// better match. To determine if the given pair is a better match, it first
|
||||
// computes the rough confidence level. If this surpasses the current match, it
|
||||
// will replace it and update the tie-breaker rule cache. If there is a tie, it
|
||||
// proceeds with applying a series of tie-breaker rules. If there is no
|
||||
// conclusive winner after applying the tie-breaker rules, it leaves the current
|
||||
// match as the preferred match.
|
||||
//
|
||||
// If pin is true and have and tag are a strong match, it will henceforth only
|
||||
// consider matches for this language. This corresponds to the nothing that most
|
||||
// users have a strong preference for the first defined language. A user can
|
||||
// still prefer a second language over a dialect of the preferred language by
|
||||
// explicitly specifying dialects, e.g. "en, nl, en-GB". In this case pin should
|
||||
// be false.
|
||||
func (m *bestMatch) update(have *haveTag, tag language.Tag, maxScript language.Script, maxRegion language.Region, pin bool) {
|
||||
// Bail if the maximum attainable confidence is below that of the current best match.
|
||||
c := have.conf
|
||||
if c < m.conf {
|
||||
return
|
||||
}
|
||||
// Don't change the language once we already have found an exact match.
|
||||
if m.pinLanguage && tag.LangID != m.want.LangID {
|
||||
return
|
||||
}
|
||||
// Pin the region group if we are comparing tags for the same language.
|
||||
if tag.LangID == m.want.LangID && m.sameRegionGroup {
|
||||
_, sameGroup := regionGroupDist(m.pinnedRegion, have.maxRegion, have.maxScript, m.want.LangID)
|
||||
if !sameGroup {
|
||||
return
|
||||
}
|
||||
}
|
||||
if c == Exact && have.maxScript == maxScript {
|
||||
// If there is another language and then another entry of this language,
|
||||
// don't pin anything, otherwise pin the language.
|
||||
m.pinLanguage = pin
|
||||
}
|
||||
if equalsRest(have.tag, tag) {
|
||||
} else if have.maxScript != maxScript {
|
||||
// There is usually very little comprehension between different scripts.
|
||||
// In a few cases there may still be Low comprehension. This possibility
|
||||
// is pre-computed and stored in have.altScript.
|
||||
if Low < m.conf || have.altScript != maxScript {
|
||||
return
|
||||
}
|
||||
c = Low
|
||||
} else if have.maxRegion != maxRegion {
|
||||
if High < c {
|
||||
// There is usually a small difference between languages across regions.
|
||||
c = High
|
||||
}
|
||||
}
|
||||
|
||||
// We store the results of the computations of the tie-breaker rules along
|
||||
// with the best match. There is no need to do the checks once we determine
|
||||
// we have a winner, but we do still need to do the tie-breaker computations.
|
||||
// We use "beaten" to keep track if we still need to do the checks.
|
||||
beaten := false // true if the new pair defeats the current one.
|
||||
if c != m.conf {
|
||||
if c < m.conf {
|
||||
return
|
||||
}
|
||||
beaten = true
|
||||
}
|
||||
|
||||
// Tie-breaker rules:
|
||||
// We prefer if the pre-maximized language was specified and identical.
|
||||
origLang := have.tag.LangID == tag.LangID && tag.LangID != 0
|
||||
if !beaten && m.origLang != origLang {
|
||||
if m.origLang {
|
||||
return
|
||||
}
|
||||
beaten = true
|
||||
}
|
||||
|
||||
// We prefer if the pre-maximized region was specified and identical.
|
||||
origReg := have.tag.RegionID == tag.RegionID && tag.RegionID != 0
|
||||
if !beaten && m.origReg != origReg {
|
||||
if m.origReg {
|
||||
return
|
||||
}
|
||||
beaten = true
|
||||
}
|
||||
|
||||
regGroupDist, sameGroup := regionGroupDist(have.maxRegion, maxRegion, maxScript, tag.LangID)
|
||||
if !beaten && m.regGroupDist != regGroupDist {
|
||||
if regGroupDist > m.regGroupDist {
|
||||
return
|
||||
}
|
||||
beaten = true
|
||||
}
|
||||
|
||||
paradigmReg := isParadigmLocale(tag.LangID, have.maxRegion)
|
||||
if !beaten && m.paradigmReg != paradigmReg {
|
||||
if !paradigmReg {
|
||||
return
|
||||
}
|
||||
beaten = true
|
||||
}
|
||||
|
||||
// Next we prefer if the pre-maximized script was specified and identical.
|
||||
origScript := have.tag.ScriptID == tag.ScriptID && tag.ScriptID != 0
|
||||
if !beaten && m.origScript != origScript {
|
||||
if m.origScript {
|
||||
return
|
||||
}
|
||||
beaten = true
|
||||
}
|
||||
|
||||
// Update m to the newly found best match.
|
||||
if beaten {
|
||||
m.have = have
|
||||
m.want = tag
|
||||
m.conf = c
|
||||
m.pinnedRegion = maxRegion
|
||||
m.sameRegionGroup = sameGroup
|
||||
m.origLang = origLang
|
||||
m.origReg = origReg
|
||||
m.paradigmReg = paradigmReg
|
||||
m.origScript = origScript
|
||||
m.regGroupDist = regGroupDist
|
||||
}
|
||||
}
|
||||
|
||||
func isParadigmLocale(lang language.Language, r language.Region) bool {
|
||||
for _, e := range paradigmLocales {
|
||||
if language.Language(e[0]) == lang && (r == language.Region(e[1]) || r == language.Region(e[2])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// regionGroupDist computes the distance between two regions based on their
|
||||
// CLDR grouping.
|
||||
func regionGroupDist(a, b language.Region, script language.Script, lang language.Language) (dist uint8, same bool) {
|
||||
const defaultDistance = 4
|
||||
|
||||
aGroup := uint(regionToGroups[a]) << 1
|
||||
bGroup := uint(regionToGroups[b]) << 1
|
||||
for _, ri := range matchRegion {
|
||||
if language.Language(ri.lang) == lang && (ri.script == 0 || language.Script(ri.script) == script) {
|
||||
group := uint(1 << (ri.group &^ 0x80))
|
||||
if 0x80&ri.group == 0 {
|
||||
if aGroup&bGroup&group != 0 { // Both regions are in the group.
|
||||
return ri.distance, ri.distance == defaultDistance
|
||||
}
|
||||
} else {
|
||||
if (aGroup|bGroup)&group == 0 { // Both regions are not in the group.
|
||||
return ri.distance, ri.distance == defaultDistance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultDistance, true
|
||||
}
|
||||
|
||||
// equalsRest compares everything except the language.
|
||||
func equalsRest(a, b language.Tag) bool {
|
||||
// TODO: don't include extensions in this comparison. To do this efficiently,
|
||||
// though, we should handle private tags separately.
|
||||
return a.ScriptID == b.ScriptID && a.RegionID == b.RegionID && a.VariantOrPrivateUseTags() == b.VariantOrPrivateUseTags()
|
||||
}
|
||||
|
||||
// isExactEquivalent returns true if canonicalizing the language will not alter
|
||||
// the script or region of a tag.
|
||||
func isExactEquivalent(l language.Language) bool {
|
||||
for _, o := range notEquivalent {
|
||||
if o == l {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var notEquivalent []language.Language
|
||||
|
||||
func init() {
|
||||
// Create a list of all languages for which canonicalization may alter the
|
||||
// script or region.
|
||||
for _, lm := range language.AliasMap {
|
||||
tag := language.Tag{LangID: language.Language(lm.From)}
|
||||
if tag, _ = canonicalize(All, tag); tag.ScriptID != 0 || tag.RegionID != 0 {
|
||||
notEquivalent = append(notEquivalent, language.Language(lm.From))
|
||||
}
|
||||
}
|
||||
// Maximize undefined regions of paradigm locales.
|
||||
for i, v := range paradigmLocales {
|
||||
t := language.Tag{LangID: language.Language(v[0])}
|
||||
max, _ := t.Maximize()
|
||||
if v[1] == 0 {
|
||||
paradigmLocales[i][1] = uint16(max.RegionID)
|
||||
}
|
||||
if v[2] == 0 {
|
||||
paradigmLocales[i][2] = uint16(max.RegionID)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user