mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-25 12:24:47 +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
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
|
||||||
github.com/OpenPeeDeeP/xdg v1.0.0
|
github.com/OpenPeeDeeP/xdg v1.0.0
|
||||||
github.com/atotto/clipboard v0.1.2
|
github.com/atotto/clipboard v0.1.2
|
||||||
github.com/aybabtme/humanlog v0.4.1
|
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-colorable v0.1.7 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.9
|
github.com/mattn/go-runewidth v0.0.9
|
||||||
github.com/mgutz/str v1.2.0
|
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/ginkgo v1.10.3 // indirect
|
||||||
github.com/onsi/gomega v1.7.1 // indirect
|
github.com/onsi/gomega v1.7.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.4.2
|
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 h1:UDLmNjCGFZZCaVMB74DqYEtXkHxnTxcr4FeJVF9uCn8=
|
||||||
github.com/OpenPeeDeeP/xdg v1.0.0/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
|
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=
|
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/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 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
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 h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
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=
|
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=
|
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-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-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 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
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=
|
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/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-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-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-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 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-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 h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw=
|
||||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI=
|
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/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-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-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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-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-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-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-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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
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-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 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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/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 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
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.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.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
|
2
main.go
2
main.go
@ -127,6 +127,6 @@ func main() {
|
|||||||
stackTrace := newErr.ErrorStack()
|
stackTrace := newErr.ErrorStack()
|
||||||
app.Log.Error(stackTrace)
|
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
|
OSCommand *oscommands.OSCommand
|
||||||
GitCommand *commands.GitCommand
|
GitCommand *commands.GitCommand
|
||||||
Gui *gui.Gui
|
Gui *gui.Gui
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.TranslationSet
|
||||||
Updater *updates.Updater // may only need this on the Gui
|
Updater *updates.Updater // may only need this on the Gui
|
||||||
ClientContext string
|
ClientContext string
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
|
|||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
app.Log = newLogger(config)
|
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
|
// if we are being called in 'demon' mode, we can just return here
|
||||||
app.ClientContext = os.Getenv("LAZYGIT_CLIENT_COMMAND")
|
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 {
|
func (app *App) validateGitVersion() error {
|
||||||
output, err := app.OSCommand.RunCommandWithOutput("git --version")
|
output, err := app.OSCommand.RunCommandWithOutput("git --version")
|
||||||
// if we get an error anywhere here we'll show the same status
|
// 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 {
|
if err != nil {
|
||||||
return minVersionError
|
return minVersionError
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ func (app *App) setupRepo() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Offer to initialize a new repository in current directory.
|
// 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')
|
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||||
if strings.Trim(response, " \n") != "y" {
|
if strings.Trim(response, " \n") != "y" {
|
||||||
// check if we have a recent repo we can open
|
// 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) {
|
func (app *App) KnownError(err error) (string, bool) {
|
||||||
errorMessage := err.Error()
|
errorMessage := err.Error()
|
||||||
|
|
||||||
knownErrorMessages := []string{app.Tr.SLocalize("minGitVersionError")}
|
knownErrorMessages := []string{app.Tr.MinGitVersionError}
|
||||||
|
|
||||||
for _, message := range knownErrorMessages {
|
for _, message := range knownErrorMessages {
|
||||||
if errorMessage == message {
|
if errorMessage == message {
|
||||||
@ -286,7 +286,7 @@ func (app *App) KnownError(err error) (string, bool) {
|
|||||||
mappings := []errorMapping{
|
mappings := []errorMapping{
|
||||||
{
|
{
|
||||||
originalError: "fatal: not a git repository",
|
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{
|
return &GitCommand{
|
||||||
Log: utils.NewDummyLog(),
|
Log: utils.NewDummyLog(),
|
||||||
OSCommand: osCommand,
|
OSCommand: osCommand,
|
||||||
Tr: i18n.NewLocalizer(utils.NewDummyLog()),
|
Tr: i18n.NewTranslationSet(utils.NewDummyLog()),
|
||||||
Config: config.NewDummyAppConfig(),
|
Config: config.NewDummyAppConfig(),
|
||||||
getGlobalGitConfig: func(string) (string, error) { return "", nil },
|
getGlobalGitConfig: func(string) (string, error) { return "", nil },
|
||||||
getLocalGitConfig: func(string) (string, error) { return "", nil },
|
getLocalGitConfig: func(string) (string, error) { return "", nil },
|
||||||
|
@ -30,7 +30,7 @@ type GitCommand struct {
|
|||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
OSCommand *oscommands.OSCommand
|
OSCommand *oscommands.OSCommand
|
||||||
Repo *gogit.Repository
|
Repo *gogit.Repository
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.TranslationSet
|
||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
getGlobalGitConfig func(string) (string, error)
|
getGlobalGitConfig func(string) (string, error)
|
||||||
getLocalGitConfig func(string) (string, error)
|
getLocalGitConfig func(string) (string, error)
|
||||||
@ -44,7 +44,7 @@ type GitCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewGitCommand it runs git commands
|
// 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
|
var repo *gogit.Repository
|
||||||
|
|
||||||
// see what our default push behaviour is
|
// see what our default push behaviour is
|
||||||
@ -64,7 +64,7 @@ func NewGitCommand(log *logrus.Entry, osCommand *oscommands.OSCommand, tr *i18n.
|
|||||||
return nil, err
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ func resolvePath(path string) (string, error) {
|
|||||||
return filepath.EvalSymlinks(path)
|
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()
|
unresolvedPath := env.GetGitDirEnv()
|
||||||
if unresolvedPath == "" {
|
if unresolvedPath == "" {
|
||||||
var err error
|
var err error
|
||||||
@ -159,7 +159,7 @@ func setupRepository(openGitRepository func(string) (*gogit.Repository, error),
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
|
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
|
return nil, err
|
||||||
|
@ -159,7 +159,7 @@ func TestSetupRepository(t *testing.T) {
|
|||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
openGitRepository func(string) (*gogit.Repository, error)
|
openGitRepository func(string) (*gogit.Repository, error)
|
||||||
sLocalize func(string) string
|
errorStr string
|
||||||
test func(*gogit.Repository, error)
|
test func(*gogit.Repository, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,9 +169,7 @@ func TestSetupRepository(t *testing.T) {
|
|||||||
func(string) (*gogit.Repository, error) {
|
func(string) (*gogit.Repository, error) {
|
||||||
return nil, fmt.Errorf(`unquoted '\' must be followed by new line`)
|
return nil, fmt.Errorf(`unquoted '\' must be followed by new line`)
|
||||||
},
|
},
|
||||||
func(string) string {
|
"error translated",
|
||||||
return "error translated"
|
|
||||||
},
|
|
||||||
func(r *gogit.Repository, err error) {
|
func(r *gogit.Repository, err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.EqualError(t, err, "error translated")
|
assert.EqualError(t, err, "error translated")
|
||||||
@ -182,7 +180,7 @@ func TestSetupRepository(t *testing.T) {
|
|||||||
func(string) (*gogit.Repository, error) {
|
func(string) (*gogit.Repository, error) {
|
||||||
return nil, fmt.Errorf("Error from inside gogit")
|
return nil, fmt.Errorf("Error from inside gogit")
|
||||||
},
|
},
|
||||||
func(string) string { return "" },
|
"",
|
||||||
func(r *gogit.Repository, err error) {
|
func(r *gogit.Repository, err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.EqualError(t, err, "Error from inside gogit")
|
assert.EqualError(t, err, "Error from inside gogit")
|
||||||
@ -196,7 +194,7 @@ func TestSetupRepository(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return r, nil
|
return r, nil
|
||||||
},
|
},
|
||||||
func(string) string { return "" },
|
"",
|
||||||
func(r *gogit.Repository, err error) {
|
func(r *gogit.Repository, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, r)
|
assert.NotNil(t, r)
|
||||||
@ -206,7 +204,7 @@ func TestSetupRepository(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
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 {
|
for _, s := range scenarios {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
s.setup()
|
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
|
Log *logrus.Entry
|
||||||
GitCommand *GitCommand
|
GitCommand *GitCommand
|
||||||
OSCommand *oscommands.OSCommand
|
OSCommand *oscommands.OSCommand
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.TranslationSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommitListBuilder builds a new commit list builder
|
// 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{
|
return &CommitListBuilder{
|
||||||
Log: log,
|
Log: log,
|
||||||
GitCommand: gitCommand,
|
GitCommand: gitCommand,
|
||||||
@ -170,7 +170,7 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
|
|||||||
if rebaseMode != "" {
|
if rebaseMode != "" {
|
||||||
currentCommit := commits[len(rebasingCommits)]
|
currentCommit := commits[len(rebasingCommits)]
|
||||||
blue := color.New(color.FgYellow)
|
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)
|
currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ func NewDummyCommitListBuilder() *CommitListBuilder {
|
|||||||
Log: utils.NewDummyLog(),
|
Log: utils.NewDummyLog(),
|
||||||
GitCommand: NewDummyGitCommandWithOSCommand(osCommand),
|
GitCommand: NewDummyGitCommandWithOSCommand(osCommand),
|
||||||
OSCommand: 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
|
// one where we handle the possibility of a credential request, and the other
|
||||||
// where we continue the rebase
|
// where we continue the rebase
|
||||||
if c.usingGpg() {
|
if c.usingGpg() {
|
||||||
return errors.New(c.Tr.SLocalize("DisabledForGPG"))
|
return errors.New(c.Tr.DisabledForGPG)
|
||||||
}
|
}
|
||||||
|
|
||||||
baseIndex := sourceCommitIdx + 1
|
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 {
|
func (c *GitCommand) PullPatchIntoIndex(commits []*models.Commit, commitIdx int, p *patch.PatchManager, stash bool) error {
|
||||||
if stash {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func (pr *PullRequest) Create(branch *models.Branch) error {
|
|||||||
branchExistsOnRemote := pr.GitCommand.CheckRemoteBranchExists(branch)
|
branchExistsOnRemote := pr.GitCommand.CheckRemoteBranchExists(branch)
|
||||||
|
|
||||||
if !branchExistsOnRemote {
|
if !branchExistsOnRemote {
|
||||||
return errors.New(pr.GitCommand.Tr.SLocalize("NoBranchOnRemote"))
|
return errors.New(pr.GitCommand.Tr.NoBranchOnRemote)
|
||||||
}
|
}
|
||||||
|
|
||||||
repoURL := pr.GitCommand.GetRemoteURL()
|
repoURL := pr.GitCommand.GetRemoteURL()
|
||||||
@ -108,7 +108,7 @@ func (pr *PullRequest) Create(branch *models.Branch) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gitService == nil {
|
if gitService == nil {
|
||||||
return errors.New(pr.GitCommand.Tr.SLocalize("UnsupportedGitService"))
|
return errors.New(pr.GitCommand.Tr.UnsupportedGitService)
|
||||||
}
|
}
|
||||||
|
|
||||||
repoInfo := getRepoInfoFromURL(repoURL)
|
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
|
// we must ensure that we have at least two commits after the selected one
|
||||||
if len(commits) <= index+2 {
|
if len(commits) <= index+2 {
|
||||||
// assuming they aren't picking the bottom commit
|
// assuming they aren't picking the bottom commit
|
||||||
return errors.New(c.Tr.SLocalize("NoRoom"))
|
return errors.New(c.Tr.NoRoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
todo := ""
|
todo := ""
|
||||||
@ -101,14 +101,14 @@ func (c *GitCommand) GenerateGenericRebaseTodo(commits []*models.Commit, actionI
|
|||||||
baseIndex := actionIndex + 1
|
baseIndex := actionIndex + 1
|
||||||
|
|
||||||
if len(commits) <= baseIndex {
|
if len(commits) <= baseIndex {
|
||||||
return "", "", errors.New(c.Tr.SLocalize("CannotRebaseOntoFirstCommit"))
|
return "", "", errors.New(c.Tr.CannotRebaseOntoFirstCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
if action == "squash" || action == "fixup" {
|
if action == "squash" || action == "fixup" {
|
||||||
baseIndex++
|
baseIndex++
|
||||||
|
|
||||||
if len(commits) <= 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
|
// one where we handle the possibility of a credential request, and the other
|
||||||
// where we continue the rebase
|
// where we continue the rebase
|
||||||
if c.usingGpg() {
|
if c.usingGpg() {
|
||||||
return errors.New(c.Tr.SLocalize("DisabledForGPG"))
|
return errors.New(c.Tr.DisabledForGPG)
|
||||||
}
|
}
|
||||||
|
|
||||||
todo, sha, err := c.GenerateGenericRebaseTodo(commits, commitIndex, "edit")
|
todo, sha, err := c.GenerateGenericRebaseTodo(commits, commitIndex, "edit")
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -28,7 +29,7 @@ func (gui *Gui) handleBranchSelect() error {
|
|||||||
var task updateTask
|
var task updateTask
|
||||||
branch := gui.getSelectedBranch()
|
branch := gui.getSelectedBranch()
|
||||||
if branch == nil {
|
if branch == nil {
|
||||||
task = gui.createRenderStringTask(gui.Tr.SLocalize("NoBranchesThisRepo"))
|
task = gui.createRenderStringTask(gui.Tr.NoBranchesThisRepo)
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmd := gui.OSCommand.ExecutableFromString(
|
||||||
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
|
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
|
||||||
@ -81,7 +82,7 @@ func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
|
return gui.createErrorPanel(gui.Tr.AlreadyCheckedOutBranch)
|
||||||
}
|
}
|
||||||
branch := gui.getSelectedBranch()
|
branch := gui.getSelectedBranch()
|
||||||
return gui.handleCheckoutRef(branch.Name, handleCheckoutRefOptions{})
|
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 {
|
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
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
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 {
|
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
|
||||||
branch := gui.getSelectedBranch()
|
branch := gui.getSelectedBranch()
|
||||||
message := gui.Tr.SLocalize("SureForceCheckout")
|
message := gui.Tr.SureForceCheckout
|
||||||
title := gui.Tr.SLocalize("ForceCheckoutBranch")
|
title := gui.Tr.ForceCheckoutBranch
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: title,
|
title: title,
|
||||||
@ -136,7 +137,7 @@ type handleCheckoutRefOptions struct {
|
|||||||
func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions) error {
|
func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions) error {
|
||||||
waitingStatus := options.WaitingStatus
|
waitingStatus := options.WaitingStatus
|
||||||
if waitingStatus == "" {
|
if waitingStatus == "" {
|
||||||
waitingStatus = gui.Tr.SLocalize("CheckingOutStatus")
|
waitingStatus = gui.Tr.CheckingOutStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdOptions := commands.CheckoutOptions{Force: false, EnvVars: options.EnvVars}
|
cmdOptions := commands.CheckoutOptions{Force: false, EnvVars: options.EnvVars}
|
||||||
@ -160,10 +161,10 @@ func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions)
|
|||||||
// offer to autostash changes
|
// offer to autostash changes
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
|
|
||||||
title: gui.Tr.SLocalize("AutoStashTitle"),
|
title: gui.Tr.AutoStashTitle,
|
||||||
prompt: gui.Tr.SLocalize("AutoStashPrompt"),
|
prompt: gui.Tr.AutoStashPrompt,
|
||||||
handleConfirm: func() error {
|
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)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
if err := gui.GitCommand.Checkout(ref, cmdOptions); err != nil {
|
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 {
|
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{
|
return gui.handleCheckoutRef(response, handleCheckoutRefOptions{
|
||||||
onRefNotFound: func(ref string) error {
|
onRefNotFound: func(ref string) error {
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
|
|
||||||
title: gui.Tr.SLocalize("BranchNotFoundTitle"),
|
title: gui.Tr.BranchNotFoundTitle,
|
||||||
prompt: fmt.Sprintf("%s %s%s", gui.Tr.SLocalize("BranchNotFoundPrompt"), ref, "?"),
|
prompt: fmt.Sprintf("%s %s%s", gui.Tr.BranchNotFoundPrompt, ref, "?"),
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.createNewBranchWithName(ref)
|
return gui.createNewBranchWithName(ref)
|
||||||
},
|
},
|
||||||
@ -243,22 +244,22 @@ func (gui *Gui) deleteBranch(force bool) error {
|
|||||||
}
|
}
|
||||||
checkedOutBranch := gui.getCheckedOutBranch()
|
checkedOutBranch := gui.getCheckedOutBranch()
|
||||||
if checkedOutBranch.Name == selectedBranch.Name {
|
if checkedOutBranch.Name == selectedBranch.Name {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("CantDeleteCheckOutBranch"))
|
return gui.createErrorPanel(gui.Tr.CantDeleteCheckOutBranch)
|
||||||
}
|
}
|
||||||
return gui.deleteNamedBranch(selectedBranch, force)
|
return gui.deleteNamedBranch(selectedBranch, force)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) deleteNamedBranch(selectedBranch *models.Branch, force bool) error {
|
func (gui *Gui) deleteNamedBranch(selectedBranch *models.Branch, force bool) error {
|
||||||
title := gui.Tr.SLocalize("DeleteBranch")
|
title := gui.Tr.DeleteBranch
|
||||||
var messageID string
|
var templateStr string
|
||||||
if force {
|
if force {
|
||||||
messageID = "ForceDeleteBranchMessage"
|
templateStr = gui.Tr.ForceDeleteBranchMessage
|
||||||
} else {
|
} else {
|
||||||
messageID = "DeleteBranchMessage"
|
templateStr = gui.Tr.DeleteBranchMessage
|
||||||
}
|
}
|
||||||
message := gui.Tr.TemplateLocalize(
|
message := utils.ResolvePlaceholderString(
|
||||||
messageID,
|
templateStr,
|
||||||
Teml{
|
map[string]string{
|
||||||
"selectedBranchName": selectedBranch.Name,
|
"selectedBranchName": selectedBranch.Name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -290,11 +291,11 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
|
|||||||
}
|
}
|
||||||
checkedOutBranchName := gui.getCheckedOutBranch().Name
|
checkedOutBranchName := gui.getCheckedOutBranch().Name
|
||||||
if checkedOutBranchName == branchName {
|
if checkedOutBranchName == branchName {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("CantMergeBranchIntoItself"))
|
return gui.createErrorPanel(gui.Tr.CantMergeBranchIntoItself)
|
||||||
}
|
}
|
||||||
prompt := gui.Tr.TemplateLocalize(
|
prompt := utils.ResolvePlaceholderString(
|
||||||
"ConfirmMerge",
|
gui.Tr.ConfirmMerge,
|
||||||
Teml{
|
map[string]string{
|
||||||
"checkedOutBranch": checkedOutBranchName,
|
"checkedOutBranch": checkedOutBranchName,
|
||||||
"selectedBranch": branchName,
|
"selectedBranch": branchName,
|
||||||
},
|
},
|
||||||
@ -302,7 +303,7 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
|
|||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
|
|
||||||
title: gui.Tr.SLocalize("MergingTitle"),
|
title: gui.Tr.MergingTitle,
|
||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
err := gui.GitCommand.Merge(branchName, commands.MergeOpts{})
|
err := gui.GitCommand.Merge(branchName, commands.MergeOpts{})
|
||||||
@ -332,11 +333,11 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
|||||||
|
|
||||||
checkedOutBranch := gui.getCheckedOutBranch().Name
|
checkedOutBranch := gui.getCheckedOutBranch().Name
|
||||||
if selectedBranchName == checkedOutBranch {
|
if selectedBranchName == checkedOutBranch {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("CantRebaseOntoSelf"))
|
return gui.createErrorPanel(gui.Tr.CantRebaseOntoSelf)
|
||||||
}
|
}
|
||||||
prompt := gui.Tr.TemplateLocalize(
|
prompt := utils.ResolvePlaceholderString(
|
||||||
"ConfirmRebase",
|
gui.Tr.ConfirmRebase,
|
||||||
Teml{
|
map[string]string{
|
||||||
"checkedOutBranch": checkedOutBranch,
|
"checkedOutBranch": checkedOutBranch,
|
||||||
"selectedBranch": selectedBranchName,
|
"selectedBranch": selectedBranchName,
|
||||||
},
|
},
|
||||||
@ -344,7 +345,7 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
|||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
|
|
||||||
title: gui.Tr.SLocalize("RebasingTitle"),
|
title: gui.Tr.RebasingTitle,
|
||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
err := gui.GitCommand.RebaseBranch(selectedBranchName)
|
err := gui.GitCommand.RebaseBranch(selectedBranchName)
|
||||||
@ -362,10 +363,10 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if branch.Pushables == "?" {
|
if branch.Pushables == "?" {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("FwdNoUpstream"))
|
return gui.createErrorPanel(gui.Tr.FwdNoUpstream)
|
||||||
}
|
}
|
||||||
if branch.Pushables != "0" {
|
if branch.Pushables != "0" {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("FwdCommitsToPush"))
|
return gui.createErrorPanel(gui.Tr.FwdCommitsToPush)
|
||||||
}
|
}
|
||||||
|
|
||||||
upstream, err := gui.GitCommand.GetUpstreamForBranch(branch.Name)
|
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]
|
remoteName := split[0]
|
||||||
remoteBranchName := strings.Join(split[1:], "/")
|
remoteBranchName := strings.Join(split[1:], "/")
|
||||||
|
|
||||||
message := gui.Tr.TemplateLocalize(
|
message := utils.ResolvePlaceholderString(
|
||||||
"Fetching",
|
gui.Tr.Fetching,
|
||||||
Teml{
|
map[string]string{
|
||||||
"from": fmt.Sprintf("%s/%s", remoteName, remoteBranchName),
|
"from": fmt.Sprintf("%s/%s", remoteName, remoteBranchName),
|
||||||
"to": branch.Name,
|
"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)
|
// way to get it to show up in the reflog)
|
||||||
|
|
||||||
promptForNewName := func() error {
|
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 {
|
if err := gui.GitCommand.RenameBranch(branch.Name, newBranchName); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
@ -441,8 +442,8 @@ func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
|
|
||||||
title: gui.Tr.SLocalize("renameBranch"),
|
title: gui.Tr.LcRenameBranch,
|
||||||
prompt: gui.Tr.SLocalize("RenameBranchWarning"),
|
prompt: gui.Tr.RenameBranchWarning,
|
||||||
handleConfirm: promptForNewName,
|
handleConfirm: promptForNewName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -462,9 +463,9 @@ func (gui *Gui) handleNewBranchOffCurrentItem() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
message := gui.Tr.TemplateLocalize(
|
message := utils.ResolvePlaceholderString(
|
||||||
"NewBranchNameBranchOff",
|
gui.Tr.NewBranchNameBranchOff,
|
||||||
Teml{
|
map[string]string{
|
||||||
"branchName": item.Description(),
|
"branchName": item.Description(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -144,10 +144,10 @@ func (gui *Gui) HandlePasteCommits() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("CherryPick"),
|
title: gui.Tr.CherryPick,
|
||||||
prompt: gui.Tr.SLocalize("SureCherryPick"),
|
prompt: gui.Tr.SureCherryPick,
|
||||||
handleConfirm: func() error {
|
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)
|
err := gui.GitCommand.CherryPickCommits(gui.State.Modes.CherryPicking.CherryPickedCommits)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
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
|
fileName := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLineIdx].Name
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("DiscardFileChangesTitle"),
|
title: gui.Tr.DiscardFileChangesTitle,
|
||||||
prompt: gui.Tr.SLocalize("DiscardFileChangesPrompt"),
|
prompt: gui.Tr.DiscardFileChangesPrompt,
|
||||||
handleConfirm: func() error {
|
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.GitCommand.DiscardOldFileChanges(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, fileName); err != nil {
|
||||||
if err := gui.handleGenericMergeCommandResult(err); err != nil {
|
if err := gui.handleGenericMergeCommandResult(err); err != nil {
|
||||||
return err
|
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 {
|
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("DiscardPatch"),
|
title: gui.Tr.DiscardPatch,
|
||||||
prompt: gui.Tr.SLocalize("DiscardPatchConfirm"),
|
prompt: gui.Tr.DiscardPatchConfirm,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
gui.GitCommand.PatchManager.Reset()
|
gui.GitCommand.PatchManager.Reset()
|
||||||
return toggleTheFile()
|
return toggleTheFile()
|
||||||
@ -184,8 +184,8 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
|
|||||||
|
|
||||||
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {
|
if gui.GitCommand.PatchManager.Active() && gui.GitCommand.PatchManager.To != commitFile.Parent {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("DiscardPatch"),
|
title: gui.Tr.DiscardPatch,
|
||||||
prompt: gui.Tr.SLocalize("DiscardPatchConfirm"),
|
prompt: gui.Tr.DiscardPatchConfirm,
|
||||||
handlersManageFocus: true,
|
handlersManageFocus: true,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
gui.GitCommand.PatchManager.Reset()
|
gui.GitCommand.PatchManager.Reset()
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// runSyncOrAsyncCommand takes the output of a command that may have returned
|
// 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 {
|
func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
|
||||||
message := gui.trimmedContent(v)
|
message := gui.trimmedContent(v)
|
||||||
if message == "" {
|
if message == "" {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("CommitWithoutMessageErr"))
|
return gui.createErrorPanel(gui.Tr.CommitWithoutMessageErr)
|
||||||
}
|
}
|
||||||
flags := ""
|
flags := ""
|
||||||
skipHookPrefix := gui.Config.GetUserConfig().Git.SkipHookPrefix
|
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 {
|
func (gui *Gui) handleCommitMessageFocused() error {
|
||||||
message := gui.Tr.TemplateLocalize(
|
message := utils.ResolvePlaceholderString(
|
||||||
"CommitMessageConfirm",
|
gui.Tr.CommitMessageConfirm,
|
||||||
Teml{
|
map[string]string{
|
||||||
"keyBindClose": "esc",
|
"keyBindClose": "esc",
|
||||||
"keyBindConfirm": "enter",
|
"keyBindConfirm": "enter",
|
||||||
"keyBindNewLine": "tab",
|
"keyBindNewLine": "tab",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
gui.renderString("options", message)
|
gui.renderString("options", message)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -35,7 +36,7 @@ func (gui *Gui) handleCommitSelect() error {
|
|||||||
var task updateTask
|
var task updateTask
|
||||||
commit := gui.getSelectedLocalCommit()
|
commit := gui.getSelectedLocalCommit()
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
task = gui.createRenderStringTask(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
task = gui.createRenderStringTask(gui.Tr.NoCommitsThisBranch)
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmd := gui.OSCommand.ExecutableFromString(
|
||||||
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.Modes.Filtering.Path),
|
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 {
|
if len(gui.State.Commits) <= 1 {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
||||||
}
|
}
|
||||||
|
|
||||||
applied, err := gui.handleMidRebaseCommand("squash")
|
applied, err := gui.handleMidRebaseCommand("squash")
|
||||||
@ -165,10 +166,10 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("Squash"),
|
title: gui.Tr.Squash,
|
||||||
prompt: gui.Tr.SLocalize("SureSquashThisCommit"),
|
prompt: gui.Tr.SureSquashThisCommit,
|
||||||
handleConfirm: func() error {
|
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")
|
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "squash")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
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 {
|
if len(gui.State.Commits) <= 1 {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("YouNoCommitsToSquash"))
|
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
||||||
}
|
}
|
||||||
|
|
||||||
applied, err := gui.handleMidRebaseCommand("fixup")
|
applied, err := gui.handleMidRebaseCommand("fixup")
|
||||||
@ -194,10 +195,10 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("Fixup"),
|
title: gui.Tr.Fixup,
|
||||||
prompt: gui.Tr.SLocalize("SureFixupThisCommit"),
|
prompt: gui.Tr.SureFixupThisCommit,
|
||||||
handleConfirm: func() error {
|
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")
|
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "fixup")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
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 {
|
if gui.State.Panels.Commits.SelectedLineIdx != 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
return gui.createErrorPanel(gui.Tr.OnlyRenameTopCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
commit := gui.getSelectedLocalCommit()
|
commit := gui.getSelectedLocalCommit()
|
||||||
@ -232,7 +233,7 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.surfaceError(err)
|
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 {
|
if err := gui.GitCommand.RenameCommit(response); err != nil {
|
||||||
return gui.surfaceError(err)
|
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
|
// 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.
|
// request us to edit the commit message when prompted.
|
||||||
if action == "reword" {
|
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 {
|
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{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("DeleteCommitTitle"),
|
title: gui.Tr.DeleteCommitTitle,
|
||||||
prompt: gui.Tr.SLocalize("DeleteCommitPrompt"),
|
prompt: gui.Tr.DeleteCommitPrompt,
|
||||||
handleConfirm: func() error {
|
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")
|
err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "drop")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
})
|
})
|
||||||
@ -333,7 +334,7 @@ func (gui *Gui) handleCommitMoveDown(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.refreshRebaseCommits()
|
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)
|
err := gui.GitCommand.MoveCommitDown(gui.State.Commits, index)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
gui.State.Panels.Commits.SelectedLineIdx++
|
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.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)
|
err := gui.GitCommand.MoveCommitDown(gui.State.Commits, index-1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
gui.State.Panels.Commits.SelectedLineIdx--
|
gui.State.Panels.Commits.SelectedLineIdx--
|
||||||
@ -382,7 +383,7 @@ func (gui *Gui) handleCommitEdit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
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")
|
err = gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "edit")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
})
|
})
|
||||||
@ -394,10 +395,10 @@ func (gui *Gui) handleCommitAmendTo(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("AmendCommitTitle"),
|
title: gui.Tr.AmendCommitTitle,
|
||||||
prompt: gui.Tr.SLocalize("AmendCommitPrompt"),
|
prompt: gui.Tr.AmendCommitPrompt,
|
||||||
handleConfirm: func() error {
|
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)
|
err := gui.GitCommand.AmendTo(gui.State.Commits[gui.State.Panels.Commits.SelectedLineIdx].Sha)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
})
|
})
|
||||||
@ -467,14 +468,16 @@ func (gui *Gui) handleCreateFixupCommit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt := utils.ResolvePlaceholderString(
|
||||||
|
gui.Tr.SureCreateFixupCommit,
|
||||||
|
map[string]string{
|
||||||
|
"commit": commit.Sha,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("CreateFixupCommit"),
|
title: gui.Tr.CreateFixupCommit,
|
||||||
prompt: gui.Tr.TemplateLocalize(
|
prompt: prompt,
|
||||||
"SureCreateFixupCommit",
|
|
||||||
Teml{
|
|
||||||
"commit": commit.Sha,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.GitCommand.CreateFixupCommit(commit.Sha); err != nil {
|
if err := gui.GitCommand.CreateFixupCommit(commit.Sha); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
@ -495,16 +498,18 @@ func (gui *Gui) handleSquashAllAboveFixupCommits(g *gocui.Gui, v *gocui.View) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt := utils.ResolvePlaceholderString(
|
||||||
|
gui.Tr.SureSquashAboveCommits,
|
||||||
|
map[string]string{
|
||||||
|
"commit": commit.Sha,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("SquashAboveCommits"),
|
title: gui.Tr.SquashAboveCommits,
|
||||||
prompt: gui.Tr.TemplateLocalize(
|
prompt: prompt,
|
||||||
"SureSquashAboveCommits",
|
|
||||||
Teml{
|
|
||||||
"commit": commit.Sha,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
handleConfirm: func() error {
|
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)
|
err := gui.GitCommand.SquashAllAboveFixupCommits(commit.Sha)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
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 {
|
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 {
|
if err := gui.GitCommand.CreateLightweightTag(response, commitSha); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
@ -540,8 +545,8 @@ func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("checkoutCommit"),
|
title: gui.Tr.LcCheckoutCommit,
|
||||||
prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"),
|
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
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 {
|
func (gui *Gui) handleCreateCommitResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
commit := gui.getSelectedLocalCommit()
|
commit := gui.getSelectedLocalCommit()
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
return gui.createErrorPanel(gui.Tr.NoCommitsThisBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createResetMenu(commit.Sha)
|
return gui.createResetMenu(commit.Sha)
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type createPopupPanelOpts struct {
|
type createPopupPanelOpts struct {
|
||||||
@ -205,9 +206,9 @@ func (gui *Gui) createPopupPanel(opts createPopupPanelOpts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||||
actions := gui.Tr.TemplateLocalize(
|
actions := utils.ResolvePlaceholderString(
|
||||||
"CloseConfirm",
|
gui.Tr.CloseConfirm,
|
||||||
Teml{
|
map[string]string{
|
||||||
"keyBindClose": "esc",
|
"keyBindClose": "esc",
|
||||||
"keyBindConfirm": "enter",
|
"keyBindConfirm": "enter",
|
||||||
},
|
},
|
||||||
@ -240,7 +241,7 @@ func (gui *Gui) createErrorPanel(message string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("Error"),
|
title: gui.Tr.Error,
|
||||||
prompt: coloredMessage,
|
prompt: coloredMessage,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type credentials chan string
|
type credentials chan string
|
||||||
@ -14,10 +15,10 @@ func (gui *Gui) promptUserForCredential(passOrUname string) string {
|
|||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
credentialsView, _ := g.View("credentials")
|
credentialsView, _ := g.View("credentials")
|
||||||
if passOrUname == "username" {
|
if passOrUname == "username" {
|
||||||
credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername")
|
credentialsView.Title = gui.Tr.CredentialsUsername
|
||||||
credentialsView.Mask = 0
|
credentialsView.Mask = 0
|
||||||
} else {
|
} else {
|
||||||
credentialsView.Title = gui.Tr.SLocalize("CredentialsPassword")
|
credentialsView.Title = gui.Tr.CredentialsPassword
|
||||||
credentialsView.Mask = '*'
|
credentialsView.Mask = '*'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,13 +54,14 @@ func (gui *Gui) handleCloseCredentialsView(g *gocui.Gui, v *gocui.View) error {
|
|||||||
func (gui *Gui) handleCredentialsViewFocused() error {
|
func (gui *Gui) handleCredentialsViewFocused() error {
|
||||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||||
|
|
||||||
message := gui.Tr.TemplateLocalize(
|
message := utils.ResolvePlaceholderString(
|
||||||
"CloseConfirm",
|
gui.Tr.CloseConfirm,
|
||||||
Teml{
|
map[string]string{
|
||||||
"keyBindClose": gui.getKeyDisplay(keybindingConfig.Universal.Return),
|
"keyBindClose": gui.getKeyDisplay(keybindingConfig.Universal.Return),
|
||||||
"keyBindConfirm": gui.getKeyDisplay(keybindingConfig.Universal.Confirm),
|
"keyBindConfirm": gui.getKeyDisplay(keybindingConfig.Universal.Confirm),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
gui.renderString("options", message)
|
gui.renderString("options", message)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -69,7 +71,7 @@ func (gui *Gui) handleCredentialsPopup(cmdErr error) {
|
|||||||
if cmdErr != nil {
|
if cmdErr != nil {
|
||||||
errMessage := cmdErr.Error()
|
errMessage := cmdErr.Error()
|
||||||
if strings.Contains(errMessage, "Invalid username or password") {
|
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
|
// we are not logging this error because it may contain a password
|
||||||
gui.createErrorPanel(errMessage)
|
gui.createErrorPanel(errMessage)
|
||||||
|
@ -62,7 +62,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
|||||||
|
|
||||||
loadingText := customCommand.LoadingText
|
loadingText := customCommand.LoadingText
|
||||||
if loadingText == "" {
|
if loadingText == "" {
|
||||||
loadingText = gui.Tr.SLocalize("runningCustomCommandStatus")
|
loadingText = gui.Tr.LcRunningCustomCommandStatus
|
||||||
}
|
}
|
||||||
return gui.WithWaitingStatus(loadingText, func() error {
|
return gui.WithWaitingStatus(loadingText, func() error {
|
||||||
gui.OSCommand.PrepareSubProcess(cmdStr)
|
gui.OSCommand.PrepareSubProcess(cmdStr)
|
||||||
|
@ -114,7 +114,7 @@ func (gui *Gui) handleCreateDiffingMenuPanel(g *gocui.Gui, v *gocui.View) error
|
|||||||
name := name
|
name := name
|
||||||
menuItems = append(menuItems, []*menuItem{
|
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 {
|
onPress: func() error {
|
||||||
gui.State.Modes.Diffing.Ref = name
|
gui.State.Modes.Diffing.Ref = name
|
||||||
// can scope this down based on current view but too lazy right now
|
// 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{
|
menuItems = append(menuItems, []*menuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("enterRefToDiff"),
|
displayString: gui.Tr.LcEnterRefToDiff,
|
||||||
onPress: func() error {
|
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)
|
gui.State.Modes.Diffing.Ref = strings.TrimSpace(response)
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
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() {
|
if gui.State.Modes.Diffing.Active() {
|
||||||
menuItems = append(menuItems, []*menuItem{
|
menuItems = append(menuItems, []*menuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("swapDiff"),
|
displayString: gui.Tr.LcSwapDiff,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
gui.State.Modes.Diffing.Reverse = !gui.State.Modes.Diffing.Reverse
|
gui.State.Modes.Diffing.Reverse = !gui.State.Modes.Diffing.Reverse
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("exitDiffMode"),
|
displayString: gui.Tr.LcExitDiffMode,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
gui.State.Modes.Diffing = Diffing{}
|
gui.State.Modes.Diffing = Diffing{}
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
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{
|
menuItems = []*menuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("submoduleStashAndReset"),
|
displayString: gui.Tr.LcSubmoduleStashAndReset,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.resetSubmodule(submodule)
|
return gui.resetSubmodule(submodule)
|
||||||
},
|
},
|
||||||
@ -38,7 +38,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
} else {
|
} else {
|
||||||
menuItems = []*menuItem{
|
menuItems = []*menuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("discardAllChanges"),
|
displayString: gui.Tr.LcDiscardAllChanges,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
if err := gui.GitCommand.DiscardAllFileChanges(file); err != nil {
|
if err := gui.GitCommand.DiscardAllFileChanges(file); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
@ -50,7 +50,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
if file.HasStagedChanges && file.HasUnstagedChanges {
|
if file.HasStagedChanges && file.HasUnstagedChanges {
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &menuItem{
|
||||||
displayString: gui.Tr.SLocalize("discardUnstagedChanges"),
|
displayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
if err := gui.GitCommand.DiscardUnstagedFileChanges(file); err != nil {
|
if err := gui.GitCommand.DiscardUnstagedFileChanges(file); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
|
@ -38,7 +38,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
|||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
main: &viewUpdateOpts{
|
main: &viewUpdateOpts{
|
||||||
title: "",
|
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)
|
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||||
|
|
||||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||||
title: gui.Tr.SLocalize("UnstagedChanges"),
|
title: gui.Tr.UnstagedChanges,
|
||||||
task: gui.createRunPtyTask(cmd),
|
task: gui.createRunPtyTask(cmd),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@ -70,11 +70,11 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
|||||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||||
|
|
||||||
refreshOpts.secondary = &viewUpdateOpts{
|
refreshOpts.secondary = &viewUpdateOpts{
|
||||||
title: gui.Tr.SLocalize("StagedChanges"),
|
title: gui.Tr.StagedChanges,
|
||||||
task: gui.createRunPtyTask(cmd),
|
task: gui.createRunPtyTask(cmd),
|
||||||
}
|
}
|
||||||
} else if !file.HasUnstagedChanges {
|
} else if !file.HasUnstagedChanges {
|
||||||
refreshOpts.main.title = gui.Tr.SLocalize("StagedChanges")
|
refreshOpts.main.title = gui.Tr.StagedChanges
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshMainViews(refreshOpts)
|
return gui.refreshMainViews(refreshOpts)
|
||||||
@ -181,7 +181,7 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error
|
|||||||
return gui.handleSwitchToMerge()
|
return gui.handleSwitchToMerge()
|
||||||
}
|
}
|
||||||
if file.HasMergeConflicts {
|
if file.HasMergeConflicts {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("FileStagingRequirements"))
|
return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
|
||||||
}
|
}
|
||||||
gui.switchContext(gui.Contexts.Staging.Context)
|
gui.switchContext(gui.Contexts.Staging.Context)
|
||||||
|
|
||||||
@ -257,8 +257,8 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
if file.Tracked {
|
if file.Tracked {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("IgnoreTracked"),
|
title: gui.Tr.IgnoreTracked,
|
||||||
prompt: gui.Tr.SLocalize("IgnoreTrackedPrompt"),
|
prompt: gui.Tr.IgnoreTrackedPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.GitCommand.Ignore(file.Name); err != nil {
|
if err := gui.GitCommand.Ignore(file.Name); err != nil {
|
||||||
return err
|
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 {
|
func (gui *Gui) handleWIPCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
||||||
skipHookPreifx := gui.Config.GetUserConfig().Git.SkipHookPrefix
|
skipHookPreifx := gui.Config.GetUserConfig().Git.SkipHookPrefix
|
||||||
if skipHookPreifx == "" {
|
if skipHookPreifx == "" {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("SkipHookPrefixNotConfigured"))
|
return gui.createErrorPanel(gui.Tr.SkipHookPrefixNotConfigured)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.renderStringSync("commitMessage", skipHookPreifx)
|
gui.renderStringSync("commitMessage", skipHookPreifx)
|
||||||
@ -315,7 +315,7 @@ func (gui *Gui) handleCommitPress() error {
|
|||||||
prefixReplace := commitPrefixConfig.Replace
|
prefixReplace := commitPrefixConfig.Replace
|
||||||
rgx, err := regexp.Compile(prefixPattern)
|
rgx, err := regexp.Compile(prefixPattern)
|
||||||
if err != nil {
|
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)
|
prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace)
|
||||||
gui.renderString("commitMessage", prefix)
|
gui.renderString("commitMessage", prefix)
|
||||||
@ -337,8 +337,8 @@ func (gui *Gui) handleCommitPress() error {
|
|||||||
|
|
||||||
func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("NoFilesStagedTitle"),
|
title: gui.Tr.NoFilesStagedTitle,
|
||||||
prompt: gui.Tr.SLocalize("NoFilesStagedPrompt"),
|
prompt: gui.Tr.NoFilesStagedPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.GitCommand.StageAll(); err != nil {
|
if err := gui.GitCommand.StageAll(); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
@ -360,14 +360,14 @@ func (gui *Gui) handleAmendCommitPress() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.State.Commits) == 0 {
|
if len(gui.State.Commits) == 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("NoCommitToAmend"))
|
return gui.createErrorPanel(gui.Tr.NoCommitToAmend)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: strings.Title(gui.Tr.SLocalize("AmendLastCommit")),
|
title: strings.Title(gui.Tr.AmendLastCommit),
|
||||||
prompt: gui.Tr.SLocalize("SureToAmend"),
|
prompt: gui.Tr.SureToAmend,
|
||||||
handleConfirm: func() error {
|
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())
|
ok, err := gui.runSyncOrAsyncCommand(gui.GitCommand.AmendHead())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err := gui.GitCommand.SetUpstreamBranch(upstream); err != nil {
|
||||||
errorMessage := err.Error()
|
errorMessage := err.Error()
|
||||||
if strings.Contains(errorMessage, "does not exist") {
|
if strings.Contains(errorMessage, "does not exist") {
|
||||||
@ -505,7 +505,7 @@ type PullFilesOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pullFiles(opts PullFilesOptions) error {
|
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
|
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 {
|
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
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
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") {
|
if err != nil && !force && strings.Contains(err.Error(), "Updates were rejected") {
|
||||||
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
||||||
if forcePushDisabled {
|
if forcePushDisabled {
|
||||||
gui.createErrorPanel(gui.Tr.SLocalize("UpdatesRejectedAndForcePushDisabled"))
|
gui.createErrorPanel(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gui.ask(askOpts{
|
gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("ForcePush"),
|
title: gui.Tr.ForcePush,
|
||||||
prompt: gui.Tr.SLocalize("ForcePushPrompt"),
|
prompt: gui.Tr.ForcePushPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.pushWithForceFlag(v, true, upstream, args)
|
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 {
|
if gui.GitCommand.PushToCurrent {
|
||||||
return gui.pushWithForceFlag(v, false, "", "--set-upstream")
|
return gui.pushWithForceFlag(v, false, "", "--set-upstream")
|
||||||
} else {
|
} 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, "")
|
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
|
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
||||||
if forcePushDisabled {
|
if forcePushDisabled {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("ForcePushDisabled"))
|
return gui.createErrorPanel(gui.Tr.ForcePushDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("ForcePush"),
|
title: gui.Tr.ForcePush,
|
||||||
prompt: gui.Tr.SLocalize("ForcePushPrompt"),
|
prompt: gui.Tr.ForcePushPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.pushWithForceFlag(v, true, "", "")
|
return gui.pushWithForceFlag(v, true, "", "")
|
||||||
},
|
},
|
||||||
@ -627,7 +627,7 @@ func (gui *Gui) handleSwitchToMerge() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !file.HasInlineMergeConflicts {
|
if !file.HasInlineMergeConflicts {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("FileNoMergeCons"))
|
return gui.createErrorPanel(gui.Tr.FileNoMergeCons)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.switchContext(gui.Contexts.Merging.Context)
|
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 {
|
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)
|
gui.SubProcess = gui.OSCommand.RunCustomCommand(command)
|
||||||
return gui.Errors.ErrSubProcess
|
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 {
|
func (gui *Gui) handleCreateStashMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
menuItems := []*menuItem{
|
menuItems := []*menuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("stashAllChanges"),
|
displayString: gui.Tr.LcStashAllChanges,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.handleStashSave(gui.GitCommand.StashSave)
|
return gui.handleStashSave(gui.GitCommand.StashSave)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("stashStagedChanges"),
|
displayString: gui.Tr.LcStashStagedChanges,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.handleStashSave(gui.GitCommand.StashSaveStagedChanges)
|
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 {
|
func (gui *Gui) handleStashChanges(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
@ -3,8 +3,8 @@ package gui
|
|||||||
func (gui *Gui) validateNotInFilterMode() (bool, error) {
|
func (gui *Gui) validateNotInFilterMode() (bool, error) {
|
||||||
if gui.State.Modes.Filtering.Active() {
|
if gui.State.Modes.Filtering.Active() {
|
||||||
err := gui.ask(askOpts{
|
err := gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("MustExitFilterModeTitle"),
|
title: gui.Tr.MustExitFilterModeTitle,
|
||||||
prompt: gui.Tr.SLocalize("MustExitFilterModePrompt"),
|
prompt: gui.Tr.MustExitFilterModePrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.exitFilterMode()
|
return gui.exitFilterMode()
|
||||||
},
|
},
|
||||||
|
@ -30,7 +30,7 @@ func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) erro
|
|||||||
|
|
||||||
if fileName != "" {
|
if fileName != "" {
|
||||||
menuItems = append(menuItems, &menuItem{
|
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 {
|
onPress: func() error {
|
||||||
gui.State.Modes.Filtering.Path = fileName
|
gui.State.Modes.Filtering.Path = fileName
|
||||||
return gui.Errors.ErrRestart
|
return gui.Errors.ErrRestart
|
||||||
@ -39,9 +39,9 @@ func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &menuItem{
|
||||||
displayString: gui.Tr.SLocalize("filterPathOption"),
|
displayString: gui.Tr.LcFilterPathOption,
|
||||||
onPress: func() error {
|
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)
|
gui.State.Modes.Filtering.Path = strings.TrimSpace(response)
|
||||||
return gui.Errors.ErrRestart
|
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() {
|
if gui.State.Modes.Filtering.Active() {
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &menuItem{
|
||||||
displayString: gui.Tr.SLocalize("exitFilterMode"),
|
displayString: gui.Tr.LcExitFilterMode,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
gui.State.Modes.Filtering.Path = ""
|
gui.State.Modes.Filtering.Path = ""
|
||||||
return gui.Errors.ErrRestart
|
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"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) error {
|
func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) error {
|
||||||
@ -28,7 +29,7 @@ func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if branchType == "" {
|
if branchType == "" {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("NotAGitFlowBranch"))
|
return gui.createErrorPanel(gui.Tr.NotAGitFlowBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "finish", suffix)
|
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 {
|
startHandler := func(branchType string) func() error {
|
||||||
return 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 {
|
return gui.prompt(title, "", func(name string) error {
|
||||||
subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "start", name)
|
subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "start", name)
|
||||||
gui.SubProcess = subProcess
|
gui.SubProcess = subProcess
|
||||||
|
@ -149,7 +149,7 @@ func (gui *Gui) handleInfoClick(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
for _, mode := range gui.modeStatuses() {
|
for _, mode := range gui.modeStatuses() {
|
||||||
if mode.isActive() {
|
if mode.isActive() {
|
||||||
if width-cx > len(gui.Tr.SLocalize("(reset)")) {
|
if width-cx > len(gui.Tr.ResetInParentheses) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return mode.reset()
|
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 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 gui.OSCommand.OpenLink("https://github.com/sponsors/jesseduffield")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -175,7 +175,7 @@ func (gui *Gui) fetch(canPromptForCredentials bool) (err error) {
|
|||||||
err = gui.GitCommand.Fetch(fetchOpts)
|
err = gui.GitCommand.Fetch(fetchOpts)
|
||||||
|
|
||||||
if canPromptForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") {
|
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})
|
gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC})
|
||||||
|
@ -67,8 +67,8 @@ type SentinelErrors struct {
|
|||||||
// localising things in the code.
|
// localising things in the code.
|
||||||
func (gui *Gui) GenerateSentinelErrors() {
|
func (gui *Gui) GenerateSentinelErrors() {
|
||||||
gui.Errors = SentinelErrors{
|
gui.Errors = SentinelErrors{
|
||||||
ErrSubProcess: errors.New(gui.Tr.SLocalize("RunningSubprocess")),
|
ErrSubProcess: errors.New(gui.Tr.RunningSubprocess),
|
||||||
ErrNoFiles: errors.New(gui.Tr.SLocalize("NoChangedFiles")),
|
ErrNoFiles: errors.New(gui.Tr.NoChangedFiles),
|
||||||
ErrSwitchRepo: errors.New("switching repo"),
|
ErrSwitchRepo: errors.New("switching repo"),
|
||||||
ErrRestart: errors.New("restarting"),
|
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
|
// Gui wraps the gocui Gui object which handles rendering and events
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
g *gocui.Gui
|
g *gocui.Gui
|
||||||
@ -95,7 +92,7 @@ type Gui struct {
|
|||||||
SubProcess *exec.Cmd
|
SubProcess *exec.Cmd
|
||||||
State *guiState
|
State *guiState
|
||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.TranslationSet
|
||||||
Errors SentinelErrors
|
Errors SentinelErrors
|
||||||
Updater *updates.Updater
|
Updater *updates.Updater
|
||||||
statusManager *statusManager
|
statusManager *statusManager
|
||||||
@ -391,7 +388,7 @@ func (gui *Gui) resetState() {
|
|||||||
|
|
||||||
// for now the split view will always be on
|
// for now the split view will always be on
|
||||||
// NewGui builds a new gui handler
|
// 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{
|
gui := &Gui{
|
||||||
Log: log,
|
Log: log,
|
||||||
GitCommand: gitCommand,
|
GitCommand: gitCommand,
|
||||||
@ -533,7 +530,7 @@ func (gui *Gui) runCommand() error {
|
|||||||
gui.SubProcess.Stdin = nil
|
gui.SubProcess.Stdin = nil
|
||||||
gui.SubProcess = 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
|
fmt.Scanln() // wait for enter press
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -580,7 +577,7 @@ func (gui *Gui) showIntroPopupMessage(done chan struct{}) error {
|
|||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: "",
|
title: "",
|
||||||
prompt: gui.Tr.SLocalize("IntroPopupMessage"),
|
prompt: gui.Tr.IntroPopupMessage,
|
||||||
handleConfirm: onConfirm,
|
handleConfirm: onConfirm,
|
||||||
handleClose: onConfirm,
|
handleClose: onConfirm,
|
||||||
})
|
})
|
||||||
@ -610,8 +607,8 @@ func (gui *Gui) startBackgroundFetch() {
|
|||||||
err := gui.fetch(false)
|
err := gui.fetch(false)
|
||||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||||
_ = gui.ask(askOpts{
|
_ = gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("NoAutomaticGitFetchTitle"),
|
title: gui.Tr.NoAutomaticGitFetchTitle,
|
||||||
prompt: gui.Tr.SLocalize("NoAutomaticGitFetchBody"),
|
prompt: gui.Tr.NoAutomaticGitFetchBody,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
gui.goEvery(time.Second*60, gui.stopChan, func() error {
|
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 {
|
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()
|
return donate + " " + gui.Config.GetVersion()
|
||||||
} else {
|
} else {
|
||||||
return gui.Config.GetVersion()
|
return gui.Config.GetVersion()
|
||||||
@ -37,7 +37,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
|
v.Title = gui.Tr.NotEnoughSpace
|
||||||
v.Wrap = true
|
v.Wrap = true
|
||||||
_, _ = g.SetViewOnTop("limit")
|
_, _ = g.SetViewOnTop("limit")
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Title = gui.Tr.SLocalize("DiffTitle")
|
v.Title = gui.Tr.DiffTitle
|
||||||
v.Wrap = true
|
v.Wrap = true
|
||||||
v.FgColor = textColor
|
v.FgColor = textColor
|
||||||
v.IgnoreCarriageReturns = true
|
v.IgnoreCarriageReturns = true
|
||||||
@ -115,7 +115,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
secondaryView.Title = gui.Tr.SLocalize("DiffTitle")
|
secondaryView.Title = gui.Tr.DiffTitle
|
||||||
secondaryView.Wrap = true
|
secondaryView.Wrap = true
|
||||||
secondaryView.FgColor = textColor
|
secondaryView.FgColor = textColor
|
||||||
secondaryView.IgnoreCarriageReturns = true
|
secondaryView.IgnoreCarriageReturns = true
|
||||||
@ -127,7 +127,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Title = gui.Tr.SLocalize("StatusTitle")
|
v.Title = gui.Tr.StatusTitle
|
||||||
v.FgColor = textColor
|
v.FgColor = textColor
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
filesView.Highlight = true
|
filesView.Highlight = true
|
||||||
filesView.Title = gui.Tr.SLocalize("FilesTitle")
|
filesView.Title = gui.Tr.FilesTitle
|
||||||
filesView.ContainsList = true
|
filesView.ContainsList = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
branchesView.Title = gui.Tr.SLocalize("BranchesTitle")
|
branchesView.Title = gui.Tr.BranchesTitle
|
||||||
branchesView.FgColor = textColor
|
branchesView.FgColor = textColor
|
||||||
branchesView.ContainsList = true
|
branchesView.ContainsList = true
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
commitFilesView.Title = gui.Tr.SLocalize("CommitFiles")
|
commitFilesView.Title = gui.Tr.CommitFiles
|
||||||
commitFilesView.FgColor = textColor
|
commitFilesView.FgColor = textColor
|
||||||
commitFilesView.ContainsList = true
|
commitFilesView.ContainsList = true
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
commitsView.Title = gui.Tr.SLocalize("CommitsTitle")
|
commitsView.Title = gui.Tr.CommitsTitle
|
||||||
commitsView.FgColor = textColor
|
commitsView.FgColor = textColor
|
||||||
commitsView.ContainsList = true
|
commitsView.ContainsList = true
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stashView.Title = gui.Tr.SLocalize("StashTitle")
|
stashView.Title = gui.Tr.StashTitle
|
||||||
stashView.FgColor = textColor
|
stashView.FgColor = textColor
|
||||||
stashView.ContainsList = true
|
stashView.ContainsList = true
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _ = g.SetViewOnBottom("commitMessage")
|
_, _ = g.SetViewOnBottom("commitMessage")
|
||||||
commitMessageView.Title = gui.Tr.SLocalize("CommitMessage")
|
commitMessageView.Title = gui.Tr.CommitMessage
|
||||||
commitMessageView.FgColor = textColor
|
commitMessageView.FgColor = textColor
|
||||||
commitMessageView.Editable = true
|
commitMessageView.Editable = true
|
||||||
commitMessageView.Editor = gocui.EditorFunc(gui.commitMessageEditor)
|
commitMessageView.Editor = gocui.EditorFunc(gui.commitMessageEditor)
|
||||||
@ -202,7 +202,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _ = g.SetViewOnBottom("credentials")
|
_, _ = g.SetViewOnBottom("credentials")
|
||||||
credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername")
|
credentialsView.Title = gui.Tr.CredentialsUsername
|
||||||
credentialsView.FgColor = textColor
|
credentialsView.FgColor = textColor
|
||||||
credentialsView.Editable = true
|
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: 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.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.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.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.SLocalize("nextPage")},
|
{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.SLocalize("gotoTop")},
|
{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, 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},
|
{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},
|
Contexts: []string{listContext.ContextKey},
|
||||||
Key: gui.getKey(keybindingConfig.Universal.StartSearch),
|
Key: gui.getKey(keybindingConfig.Universal.StartSearch),
|
||||||
Handler: openSearchHandler,
|
Handler: openSearchHandler,
|
||||||
Description: gui.Tr.SLocalize("startSearch"),
|
Description: gui.Tr.LcStartSearch,
|
||||||
Tag: "navigation",
|
Tag: "navigation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -546,7 +546,7 @@ func (gui *Gui) getListContextKeyBindings() []*Binding {
|
|||||||
Contexts: []string{listContext.ContextKey},
|
Contexts: []string{listContext.ContextKey},
|
||||||
Key: gui.getKey(keybindingConfig.Universal.GotoBottom),
|
Key: gui.getKey(keybindingConfig.Universal.GotoBottom),
|
||||||
Handler: gotoBottomHandler,
|
Handler: gotoBottomHandler,
|
||||||
Description: gui.Tr.SLocalize("gotoBottom"),
|
Description: gui.Tr.LcGotoBottom,
|
||||||
Tag: "navigation",
|
Tag: "navigation",
|
||||||
},
|
},
|
||||||
}...)
|
}...)
|
||||||
|
@ -36,9 +36,9 @@ func (gui *Gui) getMenuOptions() map[string]string {
|
|||||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||||
|
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
gui.getKeyDisplay(keybindingConfig.Universal.Return): gui.Tr.SLocalize("close"),
|
gui.getKeyDisplay(keybindingConfig.Universal.Return): gui.Tr.LcClose,
|
||||||
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.SLocalize("navigate"),
|
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevItem), gui.getKeyDisplay(keybindingConfig.Universal.NextItem)): gui.Tr.LcNavigate,
|
||||||
gui.getKeyDisplay(keybindingConfig.Universal.Select): gui.Tr.SLocalize("execute"),
|
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 {
|
if createMenuOptions.showCancel {
|
||||||
// this is mutative but I'm okay with that for now
|
// this is mutative but I'm okay with that for now
|
||||||
items = append(items, &menuItem{
|
items = append(items, &menuItem{
|
||||||
displayStrings: []string{gui.Tr.SLocalize("cancel")},
|
displayStrings: []string{gui.Tr.LcCancel},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -244,7 +244,7 @@ func (gui *Gui) refreshMergePanel() error {
|
|||||||
|
|
||||||
return gui.refreshMainViews(refreshMainOpts{
|
return gui.refreshMainViews(refreshMainOpts{
|
||||||
main: &viewUpdateOpts{
|
main: &viewUpdateOpts{
|
||||||
title: gui.Tr.SLocalize("MergeConflictsTitle"),
|
title: gui.Tr.MergeConflictsTitle,
|
||||||
task: gui.createRenderStringWithoutScrollTask(content),
|
task: gui.createRenderStringWithoutScrollTask(content),
|
||||||
noWrap: true,
|
noWrap: true,
|
||||||
},
|
},
|
||||||
@ -254,11 +254,11 @@ func (gui *Gui) refreshMergePanel() error {
|
|||||||
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
||||||
item := gui.getSelectedFile()
|
item := gui.getSelectedFile()
|
||||||
if item == nil {
|
if item == nil {
|
||||||
return "", errors.New(gui.Tr.SLocalize("NoFilesDisplay"))
|
return "", errors.New(gui.Tr.NoFilesDisplay)
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Type != "file" {
|
if item.Type != "file" {
|
||||||
return "", errors.New(gui.Tr.SLocalize("NotAFile"))
|
return "", errors.New(gui.Tr.NotAFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
cat, err := gui.GitCommand.CatFile(item.Name)
|
cat, err := gui.GitCommand.CatFile(item.Name)
|
||||||
@ -294,11 +294,11 @@ func (gui *Gui) getMergingOptions() map[string]string {
|
|||||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||||
|
|
||||||
return map[string]string{
|
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.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.SLocalize("navigateConflicts"),
|
fmt.Sprintf("%s %s", gui.getKeyDisplay(keybindingConfig.Universal.PrevBlock), gui.getKeyDisplay(keybindingConfig.Universal.NextBlock)): gui.Tr.LcNavigateConflicts,
|
||||||
gui.getKeyDisplay(keybindingConfig.Universal.Select): gui.Tr.SLocalize("pickHunk"),
|
gui.getKeyDisplay(keybindingConfig.Universal.Select): gui.Tr.LcPickHunk,
|
||||||
gui.getKeyDisplay(keybindingConfig.Main.PickBothHunks): gui.Tr.SLocalize("pickBothHunks"),
|
gui.getKeyDisplay(keybindingConfig.Main.PickBothHunks): gui.Tr.LcPickBothHunks,
|
||||||
gui.getKeyDisplay(keybindingConfig.Universal.Undo): gui.Tr.SLocalize("undo"),
|
gui.getKeyDisplay(keybindingConfig.Universal.Undo): gui.Tr.LcUndo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +342,7 @@ func (gui *Gui) promptToContinue() error {
|
|||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: "continue",
|
title: "continue",
|
||||||
prompt: gui.Tr.SLocalize("ConflictsResolved"),
|
prompt: gui.Tr.ConflictsResolved,
|
||||||
handlersManageFocus: true,
|
handlersManageFocus: true,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.switchContext(gui.Contexts.Files.Context); err != nil {
|
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,
|
isActive: gui.State.Modes.Diffing.Active,
|
||||||
description: func() string {
|
description: func() string {
|
||||||
return utils.ColoredString(
|
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,
|
color.FgMagenta,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -29,7 +29,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
|||||||
isActive: gui.State.Modes.Filtering.Active,
|
isActive: gui.State.Modes.Filtering.Active,
|
||||||
description: func() string {
|
description: func() string {
|
||||||
return utils.ColoredString(
|
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.FgRed,
|
||||||
color.Bold,
|
color.Bold,
|
||||||
)
|
)
|
||||||
@ -40,7 +40,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
|||||||
isActive: gui.GitCommand.PatchManager.Active,
|
isActive: gui.GitCommand.PatchManager.Active,
|
||||||
description: func() string {
|
description: func() string {
|
||||||
return utils.ColoredString(
|
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.FgYellow,
|
||||||
color.Bold,
|
color.Bold,
|
||||||
)
|
)
|
||||||
@ -51,7 +51,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
|||||||
isActive: gui.State.Modes.CherryPicking.Active,
|
isActive: gui.State.Modes.CherryPicking.Active,
|
||||||
description: func() string {
|
description: func() string {
|
||||||
return utils.ColoredString(
|
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,
|
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 {
|
func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
if !gui.GitCommand.PatchManager.Active() {
|
if !gui.GitCommand.PatchManager.Active() {
|
||||||
return gui.createErrorPanel(gui.Tr.SLocalize("NoPatchError"))
|
return gui.createErrorPanel(gui.Tr.NoPatchError)
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems := []*menuItem{
|
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 {
|
func (gui *Gui) getPatchCommitIndex() int {
|
||||||
@ -75,7 +75,7 @@ func (gui *Gui) getPatchCommitIndex() int {
|
|||||||
|
|
||||||
func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
|
func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
|
||||||
if gui.GitCommand.WorkingTreeState() != "normal" {
|
if gui.GitCommand.WorkingTreeState() != "normal" {
|
||||||
return false, gui.createErrorPanel(gui.Tr.SLocalize("CantPatchWhileRebasingError"))
|
return false, gui.createErrorPanel(gui.Tr.CantPatchWhileRebasingError)
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ func (gui *Gui) handleDeletePatchFromCommit() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||||
commitIndex := gui.getPatchCommitIndex()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
err := gui.GitCommand.DeletePatchesFromCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
|
err := gui.GitCommand.DeletePatchesFromCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -112,7 +112,7 @@ func (gui *Gui) handleMovePatchToSelectedCommit() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||||
commitIndex := gui.getPatchCommitIndex()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
err := gui.GitCommand.MovePatchToSelectedCommit(gui.State.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx, gui.GitCommand.PatchManager)
|
err := gui.GitCommand.MovePatchToSelectedCommit(gui.State.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx, gui.GitCommand.PatchManager)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -129,7 +129,7 @@ func (gui *Gui) handlePullPatchIntoWorkingTree() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pull := func(stash bool) 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()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
err := gui.GitCommand.PullPatchIntoIndex(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager, stash)
|
err := gui.GitCommand.PullPatchIntoIndex(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager, stash)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -138,8 +138,8 @@ func (gui *Gui) handlePullPatchIntoWorkingTree() error {
|
|||||||
|
|
||||||
if len(gui.trackedFiles()) > 0 {
|
if len(gui.trackedFiles()) > 0 {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("MustStashTitle"),
|
title: gui.Tr.MustStashTitle,
|
||||||
prompt: gui.Tr.SLocalize("MustStashWarning"),
|
prompt: gui.Tr.MustStashWarning,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return pull(true)
|
return pull(true)
|
||||||
},
|
},
|
||||||
@ -158,7 +158,7 @@ func (gui *Gui) handlePullPatchIntoNewCommit() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error {
|
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||||
commitIndex := gui.getPatchCommitIndex()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
err := gui.GitCommand.PullPatchIntoNewCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
|
err := gui.GitCommand.PullPatchIntoNewCommit(gui.State.Commits, commitIndex, gui.GitCommand.PatchManager)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
|
@ -75,7 +75,7 @@ func (gui *Gui) quit() error {
|
|||||||
if gui.Config.GetUserConfig().ConfirmOnQuit {
|
if gui.Config.GetUserConfig().ConfirmOnQuit {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: "",
|
title: "",
|
||||||
prompt: gui.Tr.SLocalize("ConfirmQuit"),
|
prompt: gui.Tr.ConfirmQuit,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gocui.ErrQuit
|
return gocui.ErrQuit
|
||||||
},
|
},
|
||||||
|
@ -26,9 +26,9 @@ func (gui *Gui) handleCreateRebaseOptionsMenu() error {
|
|||||||
|
|
||||||
var title string
|
var title string
|
||||||
if gui.GitCommand.WorkingTreeState() == "merging" {
|
if gui.GitCommand.WorkingTreeState() == "merging" {
|
||||||
title = gui.Tr.SLocalize("MergeOptionsTitle")
|
title = gui.Tr.MergeOptionsTitle
|
||||||
} else {
|
} else {
|
||||||
title = gui.Tr.SLocalize("RebaseOptionsTitle")
|
title = gui.Tr.RebaseOptionsTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||||
@ -38,7 +38,7 @@ func (gui *Gui) genericMergeCommand(command string) error {
|
|||||||
status := gui.GitCommand.WorkingTreeState()
|
status := gui.GitCommand.WorkingTreeState()
|
||||||
|
|
||||||
if status != "merging" && status != "rebasing" {
|
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)
|
commandType := strings.Replace(status, "ing", "e", 1)
|
||||||
@ -77,8 +77,8 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
|||||||
return nil
|
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") {
|
} 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{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("FoundConflictsTitle"),
|
title: gui.Tr.FoundConflictsTitle,
|
||||||
prompt: gui.Tr.SLocalize("FoundConflicts"),
|
prompt: gui.Tr.FoundConflicts,
|
||||||
handlersManageFocus: true,
|
handlersManageFocus: true,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.switchContext(gui.Contexts.Files.Context)
|
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 {
|
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{
|
err := gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("checkoutCommit"),
|
title: gui.Tr.LcCheckoutCommit,
|
||||||
prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"),
|
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -52,13 +53,13 @@ func (gui *Gui) handleDeleteRemoteBranch(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if remoteBranch == nil {
|
if remoteBranch == nil {
|
||||||
return 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{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("DeleteRemoteBranch"),
|
title: gui.Tr.DeleteRemoteBranch,
|
||||||
prompt: message,
|
prompt: message,
|
||||||
handleConfirm: func() error {
|
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 {
|
if err := gui.GitCommand.DeleteRemoteBranch(remoteBranch.RemoteName, remoteBranch.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -78,16 +79,16 @@ func (gui *Gui) handleSetBranchUpstream(g *gocui.Gui, v *gocui.View) error {
|
|||||||
selectedBranch := gui.getSelectedRemoteBranch()
|
selectedBranch := gui.getSelectedRemoteBranch()
|
||||||
checkedOutBranch := gui.getCheckedOutBranch()
|
checkedOutBranch := gui.getCheckedOutBranch()
|
||||||
|
|
||||||
message := gui.Tr.TemplateLocalize(
|
message := utils.ResolvePlaceholderString(
|
||||||
"SetUpstreamMessage",
|
gui.Tr.SetUpstreamMessage,
|
||||||
Teml{
|
map[string]string{
|
||||||
"checkedOut": checkedOutBranch.Name,
|
"checkedOut": checkedOutBranch.Name,
|
||||||
"selected": selectedBranch.FullName(),
|
"selected": selectedBranch.FullName(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("SetUpstreamTitle"),
|
title: gui.Tr.SetUpstreamTitle,
|
||||||
prompt: message,
|
prompt: message,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.GitCommand.SetBranchUpstream(selectedBranch.RemoteName, selectedBranch.Name, checkedOutBranch.Name); err != nil {
|
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 {
|
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.LcNewRemoteName, "", func(remoteName string) error {
|
||||||
return gui.prompt(gui.Tr.SLocalize("newRemoteUrl"), "", func(remoteUrl string) error {
|
return gui.prompt(gui.Tr.LcNewRemoteUrl, "", func(remoteUrl string) error {
|
||||||
if err := gui.GitCommand.AddRemote(remoteName, remoteUrl); err != nil {
|
if err := gui.GitCommand.AddRemote(remoteName, remoteUrl); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -97,8 +97,8 @@ func (gui *Gui) handleRemoveRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("removeRemote"),
|
title: gui.Tr.LcRemoveRemote,
|
||||||
prompt: gui.Tr.SLocalize("removeRemotePrompt") + " '" + remote.Name + "'?",
|
prompt: gui.Tr.LcRemoveRemotePrompt + " '" + remote.Name + "'?",
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.GitCommand.RemoveRemote(remote.Name); err != nil {
|
if err := gui.GitCommand.RemoveRemote(remote.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -115,9 +115,9 @@ func (gui *Gui) handleEditRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
editNameMessage := gui.Tr.TemplateLocalize(
|
editNameMessage := utils.ResolvePlaceholderString(
|
||||||
"editRemoteName",
|
gui.Tr.LcEditRemoteName,
|
||||||
Teml{
|
map[string]string{
|
||||||
"remoteName": remote.Name,
|
"remoteName": remote.Name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -129,9 +129,9 @@ func (gui *Gui) handleEditRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editUrlMessage := gui.Tr.TemplateLocalize(
|
editUrlMessage := utils.ResolvePlaceholderString(
|
||||||
"editRemoteUrl",
|
gui.Tr.LcEditRemoteUrl,
|
||||||
Teml{
|
map[string]string{
|
||||||
"remoteName": updatedRemoteName,
|
"remoteName": updatedRemoteName,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -157,7 +157,7 @@ func (gui *Gui) handleFetchRemote(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("FetchingRemoteStatus"), func() error {
|
return gui.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
|
||||||
gui.State.FetchMutex.Lock()
|
gui.State.FetchMutex.Lock()
|
||||||
defer gui.State.FetchMutex.Unlock()
|
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 {
|
if secondaryFocused {
|
||||||
gui.getMainView().Title = gui.Tr.SLocalize("StagedChanges")
|
gui.getMainView().Title = gui.Tr.StagedChanges
|
||||||
gui.getSecondaryView().Title = gui.Tr.SLocalize("UnstagedChanges")
|
gui.getSecondaryView().Title = gui.Tr.UnstagedChanges
|
||||||
} else {
|
} else {
|
||||||
gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges")
|
gui.getMainView().Title = gui.Tr.UnstagedChanges
|
||||||
gui.getSecondaryView().Title = gui.Tr.SLocalize("StagedChanges")
|
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
|
// 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 {
|
if !gui.Config.GetUserConfig().Gui.SkipUnstageLineWarning {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("UnstageLinesTitle"),
|
title: gui.Tr.UnstageLinesTitle,
|
||||||
prompt: gui.Tr.SLocalize("UnstageLinesPrompt"),
|
prompt: gui.Tr.UnstageLinesPrompt,
|
||||||
handlersManageFocus: true,
|
handlersManageFocus: true,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.switchContext(gui.Contexts.Staging.Context); err != nil {
|
if err := gui.switchContext(gui.Contexts.Staging.Context); err != nil {
|
||||||
|
@ -3,6 +3,7 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -20,7 +21,7 @@ func (gui *Gui) handleStashEntrySelect() error {
|
|||||||
var task updateTask
|
var task updateTask
|
||||||
stashEntry := gui.getSelectedStashEntry()
|
stashEntry := gui.getSelectedStashEntry()
|
||||||
if stashEntry == nil {
|
if stashEntry == nil {
|
||||||
task = gui.createRenderStringTask(gui.Tr.SLocalize("NoStashEntries"))
|
task = gui.createRenderStringTask(gui.Tr.NoStashEntries)
|
||||||
} else {
|
} else {
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmd := gui.OSCommand.ExecutableFromString(
|
||||||
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
|
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
|
||||||
@ -56,8 +57,8 @@ func (gui *Gui) handleStashApply(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("StashApply"),
|
title: gui.Tr.StashApply,
|
||||||
prompt: gui.Tr.SLocalize("SureApplyStashEntry"),
|
prompt: gui.Tr.SureApplyStashEntry,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return apply()
|
return apply()
|
||||||
},
|
},
|
||||||
@ -76,8 +77,8 @@ func (gui *Gui) handleStashPop(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("StashPop"),
|
title: gui.Tr.StashPop,
|
||||||
prompt: gui.Tr.SLocalize("SurePopStashEntry"),
|
prompt: gui.Tr.SurePopStashEntry,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return pop()
|
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 {
|
func (gui *Gui) handleStashDrop(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("StashDrop"),
|
title: gui.Tr.StashDrop,
|
||||||
prompt: gui.Tr.SLocalize("SureDropStashEntry"),
|
prompt: gui.Tr.SureDropStashEntry,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.stashDo("drop")
|
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 {
|
func (gui *Gui) stashDo(method string) error {
|
||||||
stashEntry := gui.getSelectedStashEntry()
|
stashEntry := gui.getSelectedStashEntry()
|
||||||
if stashEntry == nil {
|
if stashEntry == nil {
|
||||||
errorMessage := gui.Tr.TemplateLocalize(
|
errorMessage := utils.ResolvePlaceholderString(
|
||||||
"NoStashTo",
|
gui.Tr.NoStashTo,
|
||||||
Teml{
|
map[string]string{
|
||||||
"method": method,
|
"method": method,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.createErrorPanel(errorMessage)
|
return gui.createErrorPanel(errorMessage)
|
||||||
}
|
}
|
||||||
if err := gui.GitCommand.StashDo(stashEntry.Index, method); err != nil {
|
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 {
|
func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
||||||
if len(gui.trackedFiles()) == 0 && len(gui.stagedFiles()) == 0 {
|
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 {
|
if err := stashFunc(stashComment); err != nil {
|
||||||
return gui.surfaceError(err)
|
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 {
|
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
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 {
|
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{
|
err := gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("checkoutCommit"),
|
title: gui.Tr.LcCheckoutCommit,
|
||||||
prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"),
|
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
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 {
|
func (gui *Gui) removeSubmodule(submodule *models.SubmoduleConfig) error {
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("RemoveSubmodule"),
|
title: gui.Tr.RemoveSubmodule,
|
||||||
prompt: gui.Tr.SLocalizef("RemoveSubmodulePrompt", submodule.Name),
|
prompt: fmt.Sprintf(gui.Tr.RemoveSubmodulePrompt, submodule.Name),
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.GitCommand.SubmoduleDelete(submodule); err != nil {
|
if err := gui.GitCommand.SubmoduleDelete(submodule); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
@ -92,7 +92,7 @@ func (gui *Gui) removeSubmodule(submodule *models.SubmoduleConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleResetSubmodule(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)
|
return gui.resetSubmodule(submodule)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -126,12 +126,12 @@ func (gui *Gui) resetSubmodule(submodule *models.SubmoduleConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleAddSubmodule() 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)))
|
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.LcNewSubmoduleName, nameSuggestion, func(submoduleName string) error {
|
||||||
return gui.prompt(gui.Tr.SLocalize("newSubmodulePath"), submoduleName, func(submodulePath string) error {
|
return gui.prompt(gui.Tr.LcNewSubmodulePath, submoduleName, func(submodulePath string) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("addingSubmoduleStatus"), func() error {
|
return gui.WithWaitingStatus(gui.Tr.LcAddingSubmoduleStatus, func() error {
|
||||||
err := gui.GitCommand.SubmoduleAdd(submoduleName, submodulePath, submoduleUrl)
|
err := gui.GitCommand.SubmoduleAdd(submoduleName, submodulePath, submoduleUrl)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
@ -143,8 +143,8 @@ func (gui *Gui) handleAddSubmodule() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleEditSubmoduleUrl(submodule *models.SubmoduleConfig) 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.prompt(fmt.Sprintf(gui.Tr.LcUpdateSubmoduleUrl, submodule.Name), submodule.Url, func(newUrl string) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("updatingSubmoduleUrlStatus"), func() error {
|
return gui.WithWaitingStatus(gui.Tr.LcUpdatingSubmoduleUrlStatus, func() error {
|
||||||
err := gui.GitCommand.SubmoduleUpdateUrl(submodule.Name, submodule.Path, newUrl)
|
err := gui.GitCommand.SubmoduleUpdateUrl(submodule.Name, submodule.Path, newUrl)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ func (gui *Gui) handleEditSubmoduleUrl(submodule *models.SubmoduleConfig) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSubmoduleInit(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)
|
err := gui.GitCommand.SubmoduleInit(submodule.Path)
|
||||||
gui.handleCredentialsPopup(err)
|
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 {
|
func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) error {
|
||||||
menuItems := []*menuItem{
|
menuItems := []*menuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("submoduleStashAndReset"),
|
displayString: gui.Tr.LcSubmoduleStashAndReset,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.resetSubmodule(submodule)
|
return gui.resetSubmodule(submodule)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.SLocalize("removeSubmodule"),
|
displayString: gui.Tr.LcRemoveSubmodule,
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
return gui.removeSubmodule(submodule)
|
return gui.removeSubmodule(submodule)
|
||||||
},
|
},
|
||||||
@ -197,9 +197,9 @@ func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) er
|
|||||||
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||||
menuItems := []*menuItem{
|
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 {
|
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 {
|
if err := gui.OSCommand.RunCommand(gui.GitCommand.SubmoduleBulkInitCmdStr()); err != nil {
|
||||||
return gui.surfaceError(err)
|
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 {
|
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 {
|
if err := gui.OSCommand.RunCommand(gui.GitCommand.SubmoduleBulkUpdateCmdStr()); err != nil {
|
||||||
return gui.surfaceError(err)
|
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 {
|
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 {
|
if err := gui.GitCommand.ResetSubmodules(gui.State.Submodules); err != nil {
|
||||||
return gui.surfaceError(err)
|
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 {
|
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 {
|
if err := gui.OSCommand.RunCommand(gui.GitCommand.SubmoduleBulkDeinitCmdStr()); err != nil {
|
||||||
return gui.surfaceError(err)
|
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 {
|
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)
|
err := gui.GitCommand.SubmoduleUpdate(submodule.Path)
|
||||||
gui.handleCredentialsPopup(err)
|
gui.handleCredentialsPopup(err)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -64,15 +65,15 @@ func (gui *Gui) handleDeleteTag(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt := gui.Tr.TemplateLocalize(
|
prompt := utils.ResolvePlaceholderString(
|
||||||
"DeleteTagPrompt",
|
gui.Tr.DeleteTagPrompt,
|
||||||
Teml{
|
map[string]string{
|
||||||
"tagName": tag.Name,
|
"tagName": tag.Name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("DeleteTagTitle"),
|
title: gui.Tr.DeleteTagTitle,
|
||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
if err := gui.GitCommand.DeleteTag(tag.Name); err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
title := gui.Tr.TemplateLocalize(
|
title := utils.ResolvePlaceholderString(
|
||||||
"PushTagTitle",
|
gui.Tr.PushTagTitle,
|
||||||
Teml{
|
map[string]string{
|
||||||
"tagName": tag.Name,
|
"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 {
|
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
|
// leaving commit SHA blank so that we're just creating the tag for the current commit
|
||||||
if err := gui.GitCommand.CreateLightweightTag(tagName, ""); err != nil {
|
if err := gui.GitCommand.CreateLightweightTag(tagName, ""); err != nil {
|
||||||
return gui.surfaceError(err)
|
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 {
|
func (gui *Gui) reflogUndo(g *gocui.Gui, v *gocui.View) error {
|
||||||
undoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit undo]"}
|
undoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit undo]"}
|
||||||
undoingStatus := gui.Tr.SLocalize("UndoingStatus")
|
undoingStatus := gui.Tr.UndoingStatus
|
||||||
|
|
||||||
if gui.GitCommand.WorkingTreeState() == "rebasing" {
|
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) {
|
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 {
|
func (gui *Gui) reflogRedo(g *gocui.Gui, v *gocui.View) error {
|
||||||
redoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit redo]"}
|
redoEnvVars := []string{"GIT_REFLOG_ACTION=[lazygit redo]"}
|
||||||
redoingStatus := gui.Tr.SLocalize("RedoingStatus")
|
redoingStatus := gui.Tr.RedoingStatus
|
||||||
|
|
||||||
if gui.GitCommand.WorkingTreeState() == "rebasing" {
|
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) {
|
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
|
||||||
@ -167,11 +167,11 @@ func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHar
|
|||||||
if dirtyWorkingTree {
|
if dirtyWorkingTree {
|
||||||
// offer to autostash changes
|
// offer to autostash changes
|
||||||
return gui.ask(askOpts{
|
return gui.ask(askOpts{
|
||||||
title: gui.Tr.SLocalize("AutoStashTitle"),
|
title: gui.Tr.AutoStashTitle,
|
||||||
prompt: gui.Tr.SLocalize("AutoStashPrompt"),
|
prompt: gui.Tr.AutoStashPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(options.WaitingStatus, 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)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
if err := reset(); err != nil {
|
if err := reset(); err != nil {
|
||||||
|
@ -377,12 +377,12 @@ func (gui *Gui) globalOptionsMap() map[string]string {
|
|||||||
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
keybindingConfig := gui.Config.GetUserConfig().Keybinding
|
||||||
|
|
||||||
return map[string]string{
|
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", 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.SLocalize("navigate"),
|
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.SLocalize("cancel"),
|
gui.getKeyDisplay(keybindingConfig.Universal.Return): gui.Tr.LcCancel,
|
||||||
gui.getKeyDisplay(keybindingConfig.Universal.Quit): gui.Tr.SLocalize("quit"),
|
gui.getKeyDisplay(keybindingConfig.Universal.Quit): gui.Tr.LcQuit,
|
||||||
gui.getKeyDisplay(keybindingConfig.Universal.OptionMenu): gui.Tr.SLocalize("menu"),
|
gui.getKeyDisplay(keybindingConfig.Universal.OptionMenu): gui.Tr.LcMenu,
|
||||||
"1-5": gui.Tr.SLocalize("jump"),
|
"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"
|
nukeStr := "reset --hard HEAD && git clean -fd"
|
||||||
if len(gui.State.Submodules) > 0 {
|
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{
|
menuItems := []*menuItem{
|
||||||
{
|
{
|
||||||
displayStrings: []string{
|
displayStrings: []string{
|
||||||
gui.Tr.SLocalize("discardAllChangesToAllFiles"),
|
gui.Tr.LcDiscardAllChangesToAllFiles,
|
||||||
red.Sprint(nukeStr),
|
red.Sprint(nukeStr),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
@ -31,7 +31,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: []string{
|
displayStrings: []string{
|
||||||
gui.Tr.SLocalize("discardAnyUnstagedChanges"),
|
gui.Tr.LcDiscardAnyUnstagedChanges,
|
||||||
red.Sprint("git checkout -- ."),
|
red.Sprint("git checkout -- ."),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
@ -44,7 +44,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: []string{
|
displayStrings: []string{
|
||||||
gui.Tr.SLocalize("discardUntrackedFiles"),
|
gui.Tr.LcDiscardUntrackedFiles,
|
||||||
red.Sprint("git clean -fd"),
|
red.Sprint("git clean -fd"),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
@ -57,7 +57,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: []string{
|
displayStrings: []string{
|
||||||
gui.Tr.SLocalize("softReset"),
|
gui.Tr.LcSoftReset,
|
||||||
red.Sprint("git reset --soft HEAD"),
|
red.Sprint("git reset --soft HEAD"),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
onPress: func() error {
|
||||||
@ -83,7 +83,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: []string{
|
displayStrings: []string{
|
||||||
gui.Tr.SLocalize("hardReset"),
|
gui.Tr.LcHardReset,
|
||||||
red.Sprint("git reset --hard HEAD"),
|
red.Sprint("git reset --hard HEAD"),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
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
|
package i18n
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strings"
|
||||||
|
|
||||||
"github.com/cloudfoundry/jibber_jabber"
|
"github.com/cloudfoundry/jibber_jabber"
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/imdario/mergo"
|
||||||
"github.com/sirupsen/logrus"
|
"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
|
// Localizer will translate a message into the user's language
|
||||||
type Localizer struct {
|
type Localizer struct {
|
||||||
i18nLocalizer *i18n.Localizer
|
Log *logrus.Entry
|
||||||
language string
|
S TranslationSet
|
||||||
Log *logrus.Entry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalizer creates a new Localizer
|
// NewTranslationSet creates a new Localizer
|
||||||
func NewLocalizer(log *logrus.Entry) *Localizer {
|
func NewTranslationSet(log *logrus.Entry) *TranslationSet {
|
||||||
userLang := detectLanguage(jibber_jabber.DetectLanguage)
|
userLang := detectLanguage(jibber_jabber.DetectLanguage)
|
||||||
|
|
||||||
log.Info("language: " + userLang)
|
log.Info("language: " + userLang)
|
||||||
|
|
||||||
return setupLocalizer(log, userLang)
|
baseSet := englishTranslationSet()
|
||||||
}
|
|
||||||
|
|
||||||
// Localize handels the translations
|
for languageCode, translationSet := range GetTranslationSets() {
|
||||||
// expects i18n.LocalizeConfig as input: https://godoc.org/github.com/nicksnyder/go-i18n/v2/i18n#Localizer.MustLocalize
|
if strings.HasPrefix(userLang, languageCode) {
|
||||||
// output: translated string
|
_ = mergo.Merge(&baseSet, translationSet, mergo.WithOverride)
|
||||||
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 _, f := range fs {
|
return &baseSet
|
||||||
if err := f(i18nBundle); err != nil {
|
}
|
||||||
log.Fatal(err)
|
|
||||||
|
|
||||||
}
|
// 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"
|
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"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -17,11 +15,6 @@ func getDummyLog() *logrus.Entry {
|
|||||||
return log.WithField("test", "test")
|
return log.WithField("test", "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNewLocalizer is a function.
|
|
||||||
func TestNewLocalizer(t *testing.T) {
|
|
||||||
assert.NotNil(t, NewLocalizer(getDummyLog()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDetectLanguage is a function.
|
// TestDetectLanguage is a function.
|
||||||
func TestDetectLanguage(t *testing.T) {
|
func TestDetectLanguage(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
@ -48,44 +41,3 @@ func TestDetectLanguage(t *testing.T) {
|
|||||||
assert.EqualValues(t, s.expected, detectLanguage(s.langDetector))
|
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/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ type Updater struct {
|
|||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
OSCommand *oscommands.OSCommand
|
OSCommand *oscommands.OSCommand
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.TranslationSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updaterer implements the check and update methods
|
// Updaterer implements the check and update methods
|
||||||
@ -40,7 +41,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewUpdater creates a new updater
|
// 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")
|
contextLogger := log.WithField("context", "updates")
|
||||||
|
|
||||||
return &Updater{
|
return &Updater{
|
||||||
@ -106,13 +107,12 @@ func (u *Updater) checkForNewUpdate() (string, error) {
|
|||||||
u.Log.Info("New version is " + newVersion)
|
u.Log.Info("New version is " + newVersion)
|
||||||
|
|
||||||
if newVersion == currentVersion {
|
if newVersion == currentVersion {
|
||||||
return "", errors.New(u.Tr.SLocalize("OnLatestVersionErr"))
|
return "", errors.New(u.Tr.OnLatestVersionErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.majorVersionDiffers(currentVersion, newVersion) {
|
if u.majorVersionDiffers(currentVersion, newVersion) {
|
||||||
errMessage := u.Tr.TemplateLocalize(
|
errMessage := utils.ResolvePlaceholderString(
|
||||||
"MajorVersionErr",
|
u.Tr.MajorVersionErr, map[string]string{
|
||||||
i18n.Teml{
|
|
||||||
"newVersion": newVersion,
|
"newVersion": newVersion,
|
||||||
"currentVersion": currentVersion,
|
"currentVersion": currentVersion,
|
||||||
},
|
},
|
||||||
@ -126,12 +126,12 @@ func (u *Updater) checkForNewUpdate() (string, error) {
|
|||||||
}
|
}
|
||||||
u.Log.Info("Checking for resource at url " + rawUrl)
|
u.Log.Info("Checking for resource at url " + rawUrl)
|
||||||
if !u.verifyResourceFound(rawUrl) {
|
if !u.verifyResourceFound(rawUrl) {
|
||||||
errMessage := u.Tr.TemplateLocalize(
|
errMessage := utils.ResolvePlaceholderString(
|
||||||
"CouldNotFindBinaryErr",
|
u.Tr.CouldNotFindBinaryErr, map[string]string{
|
||||||
i18n.Teml{
|
|
||||||
"url": rawUrl,
|
"url": rawUrl,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return "", errors.New(errMessage)
|
return "", errors.New(errMessage)
|
||||||
}
|
}
|
||||||
u.Log.Info("Verified resource is available, ready to update")
|
u.Log.Info("Verified resource is available, ready to update")
|
||||||
|
@ -101,7 +101,8 @@ func Loader() string {
|
|||||||
// ResolvePlaceholderString populates a template with values
|
// ResolvePlaceholderString populates a template with values
|
||||||
func ResolvePlaceholderString(str string, arguments map[string]string) string {
|
func ResolvePlaceholderString(str string, arguments map[string]string) string {
|
||||||
for key, value := range arguments {
|
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
|
return str
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,44 @@ func writeString(file *os.File, str string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func localisedTitle(mApp *app.App, str string) string {
|
func localisedTitle(mApp *app.App, str string) string {
|
||||||
viewTitle := strings.Title(str) + "Title"
|
tr := mApp.Tr
|
||||||
return mApp.Tr.SLocalize(viewTitle)
|
|
||||||
|
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 {
|
func formatTitle(title string) string {
|
||||||
@ -155,14 +191,14 @@ outer:
|
|||||||
translatedView := localisedTitle(mApp, viewName)
|
translatedView := localisedTitle(mApp, viewName)
|
||||||
var title string
|
var title string
|
||||||
if contextAndView.subtitle == "" {
|
if contextAndView.subtitle == "" {
|
||||||
addendum := " " + mApp.Tr.SLocalize("Panel")
|
addendum := " " + mApp.Tr.Panel
|
||||||
if viewName == "global" || viewName == "navigation" {
|
if viewName == "global" || viewName == "navigation" {
|
||||||
addendum = ""
|
addendum = ""
|
||||||
}
|
}
|
||||||
title = fmt.Sprintf("%s%s", translatedView, addendum)
|
title = fmt.Sprintf("%s%s", translatedView, addendum)
|
||||||
} else {
|
} else {
|
||||||
translatedContextName := localisedTitle(mApp, contextAndView.subtitle)
|
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 {
|
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 {
|
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 {
|
for _, section := range bindingSections {
|
||||||
content += formatTitle(section.title)
|
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