Merge pull request #4464 from sharifelgamal/localization-poc
Add ability to localize all strings output to consolepull/4548/head
commit
37f09da483
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file scans all of minikube's code and finds all strings that need to be able to be translated.
|
||||||
|
It uses the more generic extract.TranslatableStringd, and prints all the translations
|
||||||
|
into every json file it can find in pkg/minikube/translate/translations.
|
||||||
|
|
||||||
|
Usage: from the root minikube directory, go run cmd/extract/extract.go
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/minikube/pkg/minikube/extract"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
paths := []string{"cmd", "pkg"}
|
||||||
|
functions := []string{"translate.T"}
|
||||||
|
output := "pkg/minikube/translate/translations"
|
||||||
|
err := extract.TranslatableStrings(paths, functions, output)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,7 +92,7 @@ var RootCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
if enableUpdateNotification {
|
if enableUpdateNotification {
|
||||||
notify.MaybePrintUpdateTextFromGithub(os.Stderr)
|
notify.MaybePrintUpdateTextFromGithub()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,7 +229,7 @@ func runStart(cmd *cobra.Command, args []string) {
|
||||||
// Makes minikube node ip to bypass http(s) proxy. since it is local traffic.
|
// Makes minikube node ip to bypass http(s) proxy. since it is local traffic.
|
||||||
err = proxy.ExcludeIP(ip)
|
err = proxy.ExcludeIP(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
console.ErrStyle(console.FailureType, "Failed to set NO_PROXY Env. please Use `export NO_PROXY=$NO_PROXY,%s`.", ip)
|
console.ErrStyle(console.FailureType, "Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,%s`.", ip)
|
||||||
}
|
}
|
||||||
// Save IP to configuration file for subsequent use
|
// Save IP to configuration file for subsequent use
|
||||||
config.KubernetesConfig.NodeIP = ip
|
config.KubernetesConfig.NodeIP = ip
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"k8s.io/minikube/pkg/minikube/console"
|
"k8s.io/minikube/pkg/minikube/console"
|
||||||
"k8s.io/minikube/pkg/minikube/constants"
|
"k8s.io/minikube/pkg/minikube/constants"
|
||||||
"k8s.io/minikube/pkg/minikube/machine"
|
"k8s.io/minikube/pkg/minikube/machine"
|
||||||
|
"k8s.io/minikube/pkg/minikube/translate"
|
||||||
_ "k8s.io/minikube/pkg/provision"
|
_ "k8s.io/minikube/pkg/provision"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,6 +41,6 @@ func main() {
|
||||||
}
|
}
|
||||||
console.SetOutFile(os.Stdout)
|
console.SetOutFile(os.Stdout)
|
||||||
console.SetErrFile(os.Stderr)
|
console.SetErrFile(os.Stderr)
|
||||||
console.DetermineLocale()
|
translate.DetermineLocale()
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -19,6 +19,7 @@ require (
|
||||||
github.com/fatih/color v1.7.0 // indirect
|
github.com/fatih/color v1.7.0 // indirect
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680 // indirect
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680 // indirect
|
||||||
github.com/gogo/protobuf v0.0.0-20170330071051-c0656edd0d9e // indirect
|
github.com/gogo/protobuf v0.0.0-20170330071051-c0656edd0d9e // indirect
|
||||||
|
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||||
github.com/golang/glog v0.0.0-20141105023935-44145f04b68c
|
github.com/golang/glog v0.0.0-20141105023935-44145f04b68c
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 // indirect
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 // indirect
|
||||||
github.com/google/btree v1.0.0 // indirect
|
github.com/google/btree v1.0.0 // indirect
|
||||||
|
@ -85,10 +86,10 @@ require (
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c // indirect
|
||||||
github.com/xeipuuv/gojsonschema v0.0.0-20160623135812-c539bca196be
|
github.com/xeipuuv/gojsonschema v0.0.0-20160623135812-c539bca196be
|
||||||
github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097
|
github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284
|
||||||
golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c
|
golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
|
||||||
golang.org/x/text v0.3.2
|
golang.org/x/text v0.3.2
|
||||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d // indirect
|
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d // indirect
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
|
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
|
||||||
|
|
20
go.sum
20
go.sum
|
@ -1,6 +1,8 @@
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
|
github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
|
||||||
|
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Parallels/docker-machine-parallels v1.3.0 h1:RG1fyf3v1GwXMCeHRiZkB4tL9phFZEv6ixcvRZ1raN8=
|
github.com/Parallels/docker-machine-parallels v1.3.0 h1:RG1fyf3v1GwXMCeHRiZkB4tL9phFZEv6ixcvRZ1raN8=
|
||||||
github.com/Parallels/docker-machine-parallels v1.3.0/go.mod h1:HCOMm3Hulq/xuEVQMyZOuQlA+dSZpFY5kdCTZWjMVis=
|
github.com/Parallels/docker-machine-parallels v1.3.0/go.mod h1:HCOMm3Hulq/xuEVQMyZOuQlA+dSZpFY5kdCTZWjMVis=
|
||||||
github.com/Sirupsen/logrus v0.0.0-20170822132746-89742aefa4b2 h1:k1A7eIeUk6rnX2yuagwljW/pDezkK8oSpvPumT9zdZY=
|
github.com/Sirupsen/logrus v0.0.0-20170822132746-89742aefa4b2 h1:k1A7eIeUk6rnX2yuagwljW/pDezkK8oSpvPumT9zdZY=
|
||||||
|
@ -38,6 +40,8 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680 h1:ZktWZesgun21uEDrwW7
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gogo/protobuf v0.0.0-20170330071051-c0656edd0d9e h1:ago6fNuQ6IhszPsXkeU7qRCyfsIX7L67WDybsAPkLl8=
|
github.com/gogo/protobuf v0.0.0-20170330071051-c0656edd0d9e h1:ago6fNuQ6IhszPsXkeU7qRCyfsIX7L67WDybsAPkLl8=
|
||||||
github.com/gogo/protobuf v0.0.0-20170330071051-c0656edd0d9e/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v0.0.0-20170330071051-c0656edd0d9e/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
|
||||||
|
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
|
||||||
github.com/golang/glog v0.0.0-20141105023935-44145f04b68c h1:CbdkBQ1/PiAo0FYJhQGwASD8wrgNvTdf01g6+O9tNuA=
|
github.com/golang/glog v0.0.0-20141105023935-44145f04b68c h1:CbdkBQ1/PiAo0FYJhQGwASD8wrgNvTdf01g6+O9tNuA=
|
||||||
github.com/golang/glog v0.0.0-20141105023935-44145f04b68c/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20141105023935-44145f04b68c/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
|
||||||
|
@ -125,6 +129,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.7 h1:wCBv/ZWBkVl/x3xvw4MAMXgjtYbzyNTcZXO5jpmVQuA=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.7/go.mod h1:JXS4+OKhbcwDoVTEj0sLFWL1vOwec2g/YBAxZ9owJqY=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20160923125401-bdcc175572fd h1:nEatQ6JnwCT9iYD5uqYUiFqq8tJGX25to8KVKXqya7k=
|
github.com/olekukonko/tablewriter v0.0.0-20160923125401-bdcc175572fd h1:nEatQ6JnwCT9iYD5uqYUiFqq8tJGX25to8KVKXqya7k=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20160923125401-bdcc175572fd/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20160923125401-bdcc175572fd/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
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=
|
||||||
|
@ -194,12 +200,18 @@ github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097 h1:Ucx5I1l1+TWXvqFm
|
||||||
github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097/go.mod h1:lFZSWRIpCfE/pt91hHBBpV6+x87YlCjsp+aIR2qCPPU=
|
github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097/go.mod h1:lFZSWRIpCfE/pt91hHBBpV6+x87YlCjsp+aIR2qCPPU=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
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 h1:rlLehGeYg6jfoyz/eDqDU1iRXLKfR42nnNh57ytKEWo=
|
||||||
|
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
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/oauth2 v0.0.0-20190115181402-5dab4167f31c h1:pcBdqVcrlT+A3i+tWsOROFONQyey9tisIQHI4xqVGLg=
|
golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c h1:pcBdqVcrlT+A3i+tWsOROFONQyey9tisIQHI4xqVGLg=
|
||||||
golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -208,18 +220,24 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FY
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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 h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
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=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
|
||||||
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-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g=
|
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g=
|
||||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
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 h1:97SnQk1GYRXJgvwZ8fadnxDOWfKvkNQHH3CtZntPSrM=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
|
|
@ -24,11 +24,10 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cloudfoundry-attic/jibber_jabber"
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
isatty "github.com/mattn/go-isatty"
|
isatty "github.com/mattn/go-isatty"
|
||||||
"golang.org/x/text/language"
|
|
||||||
"golang.org/x/text/message"
|
"golang.org/x/text/message"
|
||||||
|
"k8s.io/minikube/pkg/minikube/translate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// By design, this package uses global references to language and output objects, in preference
|
// By design, this package uses global references to language and output objects, in preference
|
||||||
|
@ -48,10 +47,6 @@ var (
|
||||||
outFile fdWriter
|
outFile fdWriter
|
||||||
// errFile is where Err* functions send output to. Set using SetErrFile()
|
// errFile is where Err* functions send output to. Set using SetErrFile()
|
||||||
errFile fdWriter
|
errFile fdWriter
|
||||||
// preferredLanguage is the default language messages will be output in
|
|
||||||
preferredLanguage = language.AmericanEnglish
|
|
||||||
// our default language
|
|
||||||
defaultLanguage = language.AmericanEnglish
|
|
||||||
// useColor is whether or not color output should be used, updated by Set*Writer.
|
// useColor is whether or not color output should be used, updated by Set*Writer.
|
||||||
useColor = false
|
useColor = false
|
||||||
// OverrideEnv is the environment variable used to override color/emoji usage
|
// OverrideEnv is the environment variable used to override color/emoji usage
|
||||||
|
@ -76,7 +71,7 @@ func OutStyle(style StyleEnum, format string, a ...interface{}) {
|
||||||
|
|
||||||
// Out writes a basic formatted string to stdout
|
// Out writes a basic formatted string to stdout
|
||||||
func Out(format string, a ...interface{}) {
|
func Out(format string, a ...interface{}) {
|
||||||
p := message.NewPrinter(preferredLanguage)
|
p := message.NewPrinter(translate.GetPreferredLanguage())
|
||||||
if outFile == nil {
|
if outFile == nil {
|
||||||
glog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...))
|
glog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...))
|
||||||
return
|
return
|
||||||
|
@ -104,7 +99,7 @@ func ErrStyle(style StyleEnum, format string, a ...interface{}) {
|
||||||
|
|
||||||
// Err writes a basic formatted string to stderr
|
// Err writes a basic formatted string to stderr
|
||||||
func Err(format string, a ...interface{}) {
|
func Err(format string, a ...interface{}) {
|
||||||
p := message.NewPrinter(preferredLanguage)
|
p := message.NewPrinter(translate.GetPreferredLanguage())
|
||||||
if errFile == nil {
|
if errFile == nil {
|
||||||
glog.Errorf("[unset errFile]: %s", fmt.Sprintf(format, a...))
|
glog.Errorf("[unset errFile]: %s", fmt.Sprintf(format, a...))
|
||||||
return
|
return
|
||||||
|
@ -140,30 +135,6 @@ func Failure(format string, a ...interface{}) {
|
||||||
ErrStyle(FailureType, format, a...)
|
ErrStyle(FailureType, format, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPreferredLanguageTag configures which language future messages should use.
|
|
||||||
func SetPreferredLanguageTag(l language.Tag) {
|
|
||||||
glog.Infof("Setting Language to %s ...", l)
|
|
||||||
preferredLanguage = l
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPreferredLanguage configures which language future messages should use, based on a LANG string.
|
|
||||||
func SetPreferredLanguage(s string) error {
|
|
||||||
// "C" is commonly used to denote a neutral POSIX locale. See http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02
|
|
||||||
if s == "" || s == "C" {
|
|
||||||
SetPreferredLanguageTag(defaultLanguage)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Handles "de_DE" or "de_DE.utf8"
|
|
||||||
// We don't process encodings, since Rob Pike invented utf8 and we're mostly stuck with it.
|
|
||||||
parts := strings.Split(s, ".")
|
|
||||||
l, err := language.Parse(parts[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
SetPreferredLanguageTag(l)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutFile configures which writer standard output goes to.
|
// SetOutFile configures which writer standard output goes to.
|
||||||
func SetOutFile(w fdWriter) {
|
func SetOutFile(w fdWriter) {
|
||||||
glog.Infof("Setting OutFile to fd %d ...", w.Fd())
|
glog.Infof("Setting OutFile to fd %d ...", w.Fd())
|
||||||
|
@ -178,17 +149,6 @@ func SetErrFile(w fdWriter) {
|
||||||
useColor = wantsColor(w.Fd())
|
useColor = wantsColor(w.Fd())
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetermineLocale() {
|
|
||||||
locale, err := jibber_jabber.DetectIETF()
|
|
||||||
if err != nil {
|
|
||||||
glog.Warningf("Getting system locale failed: %s", err)
|
|
||||||
locale = ""
|
|
||||||
}
|
|
||||||
if err := SetPreferredLanguage(locale); err != nil {
|
|
||||||
glog.Errorf("Unable to set preferred language: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// wantsColor determines if the user might want colorized output.
|
// wantsColor determines if the user might want colorized output.
|
||||||
func wantsColor(fd uintptr) bool {
|
func wantsColor(fd uintptr) bool {
|
||||||
// First process the environment: we allow users to force colors on or off.
|
// First process the environment: we allow users to force colors on or off.
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
package console
|
package console
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -25,31 +24,13 @@ import (
|
||||||
|
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
"golang.org/x/text/message"
|
"golang.org/x/text/message"
|
||||||
|
"k8s.io/minikube/pkg/minikube/tests"
|
||||||
|
"k8s.io/minikube/pkg/minikube/translate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeFile satisfies fdWriter
|
|
||||||
type fakeFile struct {
|
|
||||||
b bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFakeFile() *fakeFile {
|
|
||||||
return &fakeFile{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeFile) Fd() uintptr {
|
|
||||||
return uintptr(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fakeFile) Write(p []byte) (int, error) {
|
|
||||||
return f.b.Write(p)
|
|
||||||
}
|
|
||||||
func (f *fakeFile) String() string {
|
|
||||||
return f.b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOutStyle(t *testing.T) {
|
func TestOutStyle(t *testing.T) {
|
||||||
|
|
||||||
var tests = []struct {
|
var testCases = []struct {
|
||||||
style StyleEnum
|
style StyleEnum
|
||||||
message string
|
message string
|
||||||
params []interface{}
|
params []interface{}
|
||||||
|
@ -64,12 +45,12 @@ func TestOutStyle(t *testing.T) {
|
||||||
{Issue, "http://i/%d", []interface{}{10000}, " ▪ http://i/10000\n", " - http://i/10000\n"},
|
{Issue, "http://i/%d", []interface{}{10000}, " ▪ http://i/10000\n", " - http://i/10000\n"},
|
||||||
{Usage, "raw: %s %s", []interface{}{"'%'", "%d"}, "💡 raw: '%' %d\n", "* raw: '%' %d\n"},
|
{Usage, "raw: %s %s", []interface{}{"'%'", "%d"}, "💡 raw: '%' %d\n", "* raw: '%' %d\n"},
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range testCases {
|
||||||
for _, override := range []bool{true, false} {
|
for _, override := range []bool{true, false} {
|
||||||
t.Run(fmt.Sprintf("%s-override-%v", tc.message, override), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s-override-%v", tc.message, override), func(t *testing.T) {
|
||||||
// Set MINIKUBE_IN_STYLE=<override>
|
// Set MINIKUBE_IN_STYLE=<override>
|
||||||
os.Setenv(OverrideEnv, strconv.FormatBool(override))
|
os.Setenv(OverrideEnv, strconv.FormatBool(override))
|
||||||
f := newFakeFile()
|
f := tests.NewFakeFile()
|
||||||
SetOutFile(f)
|
SetOutFile(f)
|
||||||
OutStyle(tc.style, tc.message, tc.params...)
|
OutStyle(tc.style, tc.message, tc.params...)
|
||||||
got := f.String()
|
got := f.String()
|
||||||
|
@ -93,21 +74,23 @@ func TestOut(t *testing.T) {
|
||||||
t.Fatalf("setstring: %v", err)
|
t.Fatalf("setstring: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var tests = []struct {
|
var testCases = []struct {
|
||||||
format string
|
format string
|
||||||
lang language.Tag
|
lang string
|
||||||
arg interface{}
|
arg interface{}
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{format: "xyz123", want: "xyz123"},
|
{format: "xyz123", want: "xyz123"},
|
||||||
{format: "Installing Kubernetes version %s ...", lang: language.Arabic, arg: "v1.13", want: "... v1.13 تثبيت Kubernetes الإصدار"},
|
{format: "Installing Kubernetes version %s ...", lang: "ar", arg: "v1.13", want: "... v1.13 تثبيت Kubernetes الإصدار"},
|
||||||
{format: "Installing Kubernetes version %s ...", lang: language.AmericanEnglish, arg: "v1.13", want: "Installing Kubernetes version v1.13 ..."},
|
{format: "Installing Kubernetes version %s ...", lang: "en-us", arg: "v1.13", want: "Installing Kubernetes version v1.13 ..."},
|
||||||
{format: "Parameter encoding: %s", arg: "%s%%%d", want: "Parameter encoding: %s%%%d"},
|
{format: "Parameter encoding: %s", arg: "%s%%%d", want: "Parameter encoding: %s%%%d"},
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.format, func(t *testing.T) {
|
t.Run(tc.format, func(t *testing.T) {
|
||||||
SetPreferredLanguageTag(tc.lang)
|
if err := translate.SetPreferredLanguage(tc.lang); err != nil {
|
||||||
f := newFakeFile()
|
t.Errorf("unexpected error: %q", err)
|
||||||
|
}
|
||||||
|
f := tests.NewFakeFile()
|
||||||
SetOutFile(f)
|
SetOutFile(f)
|
||||||
ErrLn("unrelated message")
|
ErrLn("unrelated message")
|
||||||
Out(tc.format, tc.arg)
|
Out(tc.format, tc.arg)
|
||||||
|
@ -121,7 +104,7 @@ func TestOut(t *testing.T) {
|
||||||
|
|
||||||
func TestErr(t *testing.T) {
|
func TestErr(t *testing.T) {
|
||||||
os.Setenv(OverrideEnv, "0")
|
os.Setenv(OverrideEnv, "0")
|
||||||
f := newFakeFile()
|
f := tests.NewFakeFile()
|
||||||
SetErrFile(f)
|
SetErrFile(f)
|
||||||
Err("xyz123 %s\n", "%s%%%d")
|
Err("xyz123 %s\n", "%s%%%d")
|
||||||
OutLn("unrelated message")
|
OutLn("unrelated message")
|
||||||
|
@ -135,7 +118,7 @@ func TestErr(t *testing.T) {
|
||||||
|
|
||||||
func TestErrStyle(t *testing.T) {
|
func TestErrStyle(t *testing.T) {
|
||||||
os.Setenv(OverrideEnv, "1")
|
os.Setenv(OverrideEnv, "1")
|
||||||
f := newFakeFile()
|
f := tests.NewFakeFile()
|
||||||
SetErrFile(f)
|
SetErrFile(f)
|
||||||
ErrStyle(FatalType, "error: %s", "%s%%%d")
|
ErrStyle(FatalType, "error: %s", "%s%%%d")
|
||||||
got := f.String()
|
got := f.String()
|
||||||
|
@ -159,14 +142,15 @@ func TestSetPreferredLanguage(t *testing.T) {
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(tc.input, func(t *testing.T) {
|
t.Run(tc.input, func(t *testing.T) {
|
||||||
// Set something so that we can assert change.
|
// Set something so that we can assert change.
|
||||||
SetPreferredLanguageTag(language.Icelandic)
|
if err := translate.SetPreferredLanguage("is"); err != nil {
|
||||||
if err := SetPreferredLanguage(tc.input); err != nil {
|
t.Errorf("unexpected error: %q", err)
|
||||||
|
}
|
||||||
|
if err := translate.SetPreferredLanguage(tc.input); err != nil {
|
||||||
t.Errorf("unexpected error: %q", err)
|
t.Errorf("unexpected error: %q", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just compare the bases ("en", "fr"), since I can't seem to refer directly to them
|
|
||||||
want, _ := tc.want.Base()
|
want, _ := tc.want.Base()
|
||||||
got, _ := preferredLanguage.Base()
|
got, _ := translate.GetPreferredLanguage().Base()
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("SetPreferredLanguage(%s) = %q, want %q", tc.input, got, want)
|
t.Errorf("SetPreferredLanguage(%s) = %q, want %q", tc.input, got, want)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/text/message"
|
"golang.org/x/text/message"
|
||||||
"golang.org/x/text/number"
|
"golang.org/x/text/number"
|
||||||
|
"k8s.io/minikube/pkg/minikube/translate"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -131,12 +132,13 @@ func lowPrefix(s style) string {
|
||||||
|
|
||||||
// Apply styling to a format string
|
// Apply styling to a format string
|
||||||
func applyStyle(style StyleEnum, useColor bool, format string, a ...interface{}) string {
|
func applyStyle(style StyleEnum, useColor bool, format string, a ...interface{}) string {
|
||||||
p := message.NewPrinter(preferredLanguage)
|
p := message.NewPrinter(translate.GetPreferredLanguage())
|
||||||
for i, x := range a {
|
for i, x := range a {
|
||||||
if _, ok := x.(int); ok {
|
if _, ok := x.(int); ok {
|
||||||
a[i] = number.Decimal(x, number.NoSeparator())
|
a[i] = number.Decimal(x, number.NoSeparator())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
format = translate.T(format)
|
||||||
out := p.Sprintf(format, a...)
|
out := p.Sprintf(format, a...)
|
||||||
|
|
||||||
s, ok := styles[style]
|
s, ok := styles[style]
|
||||||
|
|
|
@ -99,7 +99,7 @@ func displayError(msg string, err error) {
|
||||||
// use Warning because Error will display a duplicate message to stderr
|
// use Warning because Error will display a duplicate message to stderr
|
||||||
glog.Warningf(fmt.Sprintf("%s: %v", msg, err))
|
glog.Warningf(fmt.Sprintf("%s: %v", msg, err))
|
||||||
console.Err("\n")
|
console.Err("\n")
|
||||||
console.Fatal(msg+": %v", err)
|
console.Fatal("%s: %v", msg, err)
|
||||||
console.Err("\n")
|
console.Err("\n")
|
||||||
console.ErrStyle(console.Sad, "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:")
|
console.ErrStyle(console.Sad, "Sorry that minikube crashed. If this was unexpected, we would love to hear from you:")
|
||||||
console.ErrStyle(console.URL, "https://github.com/kubernetes/minikube/issues/new")
|
console.ErrStyle(console.URL, "https://github.com/kubernetes/minikube/issues/new")
|
||||||
|
|
|
@ -0,0 +1,387 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package extract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang-collections/collections/stack"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// blacklist is a list of strings to explicitly omit from translation files.
|
||||||
|
var blacklist = []string{
|
||||||
|
"%s: %v",
|
||||||
|
"%s.%s=%s",
|
||||||
|
"%s/%d",
|
||||||
|
"%s=%s",
|
||||||
|
"%v",
|
||||||
|
}
|
||||||
|
|
||||||
|
// state is a struct that represent the current state of the extraction process
|
||||||
|
type state struct {
|
||||||
|
// The list of functions to check for
|
||||||
|
funcs map[funcType]struct{}
|
||||||
|
|
||||||
|
// A stack representation of funcs for easy iteration
|
||||||
|
fs *stack.Stack
|
||||||
|
|
||||||
|
// The list of translatable strings, in map form for easy json marhsalling
|
||||||
|
translations map[string]interface{}
|
||||||
|
|
||||||
|
// The function call we're currently checking for
|
||||||
|
currentFunc funcType
|
||||||
|
|
||||||
|
// The function we're currently parsing
|
||||||
|
parentFunc funcType
|
||||||
|
|
||||||
|
// The file we're currently checking
|
||||||
|
filename string
|
||||||
|
|
||||||
|
// The package we're currently in
|
||||||
|
currentPackage string
|
||||||
|
}
|
||||||
|
|
||||||
|
type funcType struct {
|
||||||
|
pack string // The package the function is in
|
||||||
|
name string // The name of the function
|
||||||
|
}
|
||||||
|
|
||||||
|
// newExtractor initializes state for extraction
|
||||||
|
func newExtractor(functionsToCheck []string) (*state, error) {
|
||||||
|
funcs := make(map[funcType]struct{})
|
||||||
|
fs := stack.New()
|
||||||
|
|
||||||
|
for _, t := range functionsToCheck {
|
||||||
|
// Functions must be of the form "package.function"
|
||||||
|
t2 := strings.Split(t, ".")
|
||||||
|
if len(t2) < 2 {
|
||||||
|
return nil, errors.Wrap(nil, fmt.Sprintf("Invalid function string %s. Needs package name as well.", t))
|
||||||
|
}
|
||||||
|
f := funcType{
|
||||||
|
pack: t2[0],
|
||||||
|
name: t2[1],
|
||||||
|
}
|
||||||
|
funcs[f] = struct{}{}
|
||||||
|
fs.Push(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &state{
|
||||||
|
funcs: funcs,
|
||||||
|
fs: fs,
|
||||||
|
translations: make(map[string]interface{}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetParentFunc Sets the current parent function, along with package information
|
||||||
|
func setParentFunc(e *state, f string) {
|
||||||
|
e.parentFunc = funcType{
|
||||||
|
pack: e.currentPackage,
|
||||||
|
name: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranslatableStrings finds all strings to that need to be translated in paths and prints them out to all json files in output
|
||||||
|
func TranslatableStrings(paths []string, functions []string, output string) error {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Getting current working directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(cwd, "cmd") {
|
||||||
|
fmt.Println("Run extract.go from the minikube root directory.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
e, err := newExtractor(functions)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Initializing")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Compiling translation strings...")
|
||||||
|
for e.fs.Len() > 0 {
|
||||||
|
f := e.fs.Pop().(funcType)
|
||||||
|
e.currentFunc = f
|
||||||
|
for _, root := range paths {
|
||||||
|
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if shouldCheckFile(path) {
|
||||||
|
e.filename = path
|
||||||
|
return inspectFile(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Extracting strings")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeStringsToFiles(e, output)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Writing translation files")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Done!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldCheckFile(path string) bool {
|
||||||
|
return strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "_test.go")
|
||||||
|
}
|
||||||
|
|
||||||
|
// inspectFile goes through the given file line by line looking for translatable strings
|
||||||
|
func inspectFile(e *state) error {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
r, err := ioutil.ReadFile(e.filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := parser.ParseFile(fset, "", r, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.Inspect(file, func(x ast.Node) bool {
|
||||||
|
if fi, ok := x.(*ast.File); ok {
|
||||||
|
e.currentPackage = fi.Name.String()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fd, ok := x.(*ast.FuncDecl); ok {
|
||||||
|
setParentFunc(e, fd.Name.String())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNode(x, e)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkNode checks each node to see if it's a function call
|
||||||
|
func checkNode(stmt ast.Node, e *state) {
|
||||||
|
// This line is a function call, that's what we care about
|
||||||
|
if expr, ok := stmt.(*ast.CallExpr); ok {
|
||||||
|
checkCallExpression(expr, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkCallExpression takes a function call, and checks its arguments for strings
|
||||||
|
func checkCallExpression(s *ast.CallExpr, e *state) {
|
||||||
|
for _, arg := range s.Args {
|
||||||
|
// This argument is a function literal, check its body.
|
||||||
|
if fl, ok := arg.(*ast.FuncLit); ok {
|
||||||
|
for _, stmt := range fl.Body.List {
|
||||||
|
checkNode(stmt, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var functionName string
|
||||||
|
var packageName string
|
||||||
|
|
||||||
|
// SelectorExpr is a function call to a separate package
|
||||||
|
sf, ok := s.Fun.(*ast.SelectorExpr)
|
||||||
|
if ok {
|
||||||
|
// Parse out the package of the call
|
||||||
|
sfi, ok := sf.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
packageName = sfi.Name
|
||||||
|
functionName = sf.Sel.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ident is an identifier, in this case it's a function call in the same package
|
||||||
|
id, ok := s.Fun.(*ast.Ident)
|
||||||
|
if ok {
|
||||||
|
functionName = id.Name
|
||||||
|
packageName = e.currentPackage
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not a function call.
|
||||||
|
if len(functionName) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not the correct function call, or it was called with no arguments.
|
||||||
|
if e.currentFunc.name != functionName || e.currentFunc.pack != packageName || len(s.Args) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matched := false
|
||||||
|
for _, arg := range s.Args {
|
||||||
|
// This argument is an identifier.
|
||||||
|
if i, ok := arg.(*ast.Ident); ok {
|
||||||
|
if checkIdentForStringValue(i, e) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This argument is a string.
|
||||||
|
if argString, ok := arg.(*ast.BasicLit); ok {
|
||||||
|
if addStringToList(argString.Value, e) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
addParentFuncToList(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkIdentForStringValye takes a identifier and sees if it's a variable assigned to a string
|
||||||
|
func checkIdentForStringValue(i *ast.Ident, e *state) bool {
|
||||||
|
// This identifier is nil
|
||||||
|
if i.Obj == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
as, ok := i.Obj.Decl.(*ast.AssignStmt)
|
||||||
|
|
||||||
|
// This identifier wasn't assigned anything
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
rhs, ok := as.Rhs[0].(*ast.BasicLit)
|
||||||
|
|
||||||
|
// This identifier was not assigned a string/basic value
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if addStringToList(rhs.Value, e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// addStringToList takes a string, makes sure it's meant to be translated then adds it to the list if so
|
||||||
|
func addStringToList(s string, e *state) bool {
|
||||||
|
// Empty strings don't need translating
|
||||||
|
if len(s) <= 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse out quote marks
|
||||||
|
stringToTranslate := s[1 : len(s)-1]
|
||||||
|
|
||||||
|
// Don't translate integers
|
||||||
|
if _, err := strconv.Atoi(stringToTranslate); err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't translate URLs
|
||||||
|
if u, err := url.Parse(stringToTranslate); err == nil && u.Scheme != "" && u.Host != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't translate commands
|
||||||
|
if strings.HasPrefix(stringToTranslate, "sudo ") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't translate blacklisted strings
|
||||||
|
for _, b := range blacklist {
|
||||||
|
if b == stringToTranslate {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooray, we can translate the string!
|
||||||
|
e.translations[stringToTranslate] = ""
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeStringsToFiles writes translations to all translation files in output
|
||||||
|
func writeStringsToFiles(e *state, output string) error {
|
||||||
|
err := filepath.Walk(output, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "accessing path")
|
||||||
|
}
|
||||||
|
if info.Mode().IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(path, ".json") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fmt.Printf("Writing to %s\n", filepath.Base(path))
|
||||||
|
var currentTranslations map[string]interface{}
|
||||||
|
f, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "reading translation file")
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(f, ¤tTranslations)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unmarshalling current translations")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure to not overwrite already translated strings
|
||||||
|
for k := range e.translations {
|
||||||
|
if _, ok := currentTranslations[k]; !ok {
|
||||||
|
currentTranslations[k] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove translations from the file that are empty and were not extracted
|
||||||
|
for k, v := range currentTranslations {
|
||||||
|
if _, ok := e.translations[k]; !ok && len(v.(string)) == 0 {
|
||||||
|
delete(currentTranslations, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := json.MarshalIndent(currentTranslations, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "marshalling translations")
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(path, c, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "writing translation file")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// addParentFuncToList adds the current parent function to the list of functions to inspect more closely.
|
||||||
|
func addParentFuncToList(e *state) {
|
||||||
|
if _, ok := e.funcs[e.parentFunc]; !ok {
|
||||||
|
e.funcs[e.parentFunc] = struct{}{}
|
||||||
|
e.fs.Push(e.parentFunc)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package extract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExtract(t *testing.T) {
|
||||||
|
// The file to scan
|
||||||
|
paths := []string{"testdata/sample_file.go"}
|
||||||
|
|
||||||
|
// The function we care about
|
||||||
|
functions := []string{"extract.PrintToScreen"}
|
||||||
|
|
||||||
|
// The directory where the sample translation file is in
|
||||||
|
output := "testdata/"
|
||||||
|
|
||||||
|
expected := map[string]interface{}{
|
||||||
|
"Hint: This is not a URL, come on.": "",
|
||||||
|
"Holy cow I'm in a loop!": "Something else",
|
||||||
|
"This is a variable with a string assigned": "",
|
||||||
|
"This was a choice: %s": "Something",
|
||||||
|
"Wow another string: %s": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := TranslatableStrings(paths, functions, output)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error translating strings: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var got map[string]interface{}
|
||||||
|
f, err := ioutil.ReadFile("testdata/test.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reading json file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(f, &got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error unmarshalling json: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected, got) {
|
||||||
|
t.Fatalf("Translation JSON not equal: expected %v, got %v", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package extract
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func DoSomeStuff() {
|
||||||
|
// Test with a URL
|
||||||
|
PrintToScreenNoInterface("http://kubernetes.io")
|
||||||
|
|
||||||
|
// Test with something that Go thinks looks like a URL
|
||||||
|
PrintToScreenNoInterface("Hint: This is not a URL, come on.")
|
||||||
|
|
||||||
|
// Try with an integer
|
||||||
|
PrintToScreenNoInterface("5")
|
||||||
|
|
||||||
|
// Try with a sudo command
|
||||||
|
PrintToScreenNoInterface("sudo ls .")
|
||||||
|
|
||||||
|
DoSomeOtherStuff(true, 4, "I think this should work")
|
||||||
|
|
||||||
|
v := "This is a variable with a string assigned"
|
||||||
|
PrintToScreenNoInterface(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoSomeOtherStuff(choice bool, i int, s string) {
|
||||||
|
|
||||||
|
// Let's try an if statement
|
||||||
|
if choice {
|
||||||
|
PrintToScreen("This was a choice: %s", s)
|
||||||
|
} else if i > 5 {
|
||||||
|
PrintToScreen("Wow another string: %s", i)
|
||||||
|
} else {
|
||||||
|
// Also try a loop
|
||||||
|
for i > 10 {
|
||||||
|
PrintToScreenNoInterface("Holy cow I'm in a loop!")
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintToScreenNoInterface(s string) {
|
||||||
|
PrintToScreen(s, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will be the function we'll focus the extractor on
|
||||||
|
func PrintToScreen(s string, i interface{}) {
|
||||||
|
fmt.Printf(s, i)
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Holy cow I'm in a loop!": "Something else",
|
||||||
|
"This was a choice: %s": "Something"
|
||||||
|
}
|
|
@ -19,7 +19,6 @@ package notify
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -31,6 +30,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"k8s.io/minikube/pkg/minikube/config"
|
"k8s.io/minikube/pkg/minikube/config"
|
||||||
|
"k8s.io/minikube/pkg/minikube/console"
|
||||||
"k8s.io/minikube/pkg/minikube/constants"
|
"k8s.io/minikube/pkg/minikube/constants"
|
||||||
"k8s.io/minikube/pkg/version"
|
"k8s.io/minikube/pkg/version"
|
||||||
)
|
)
|
||||||
|
@ -43,12 +43,12 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaybePrintUpdateTextFromGithub prints update text if needed, from github
|
// MaybePrintUpdateTextFromGithub prints update text if needed, from github
|
||||||
func MaybePrintUpdateTextFromGithub(output io.Writer) {
|
func MaybePrintUpdateTextFromGithub() {
|
||||||
MaybePrintUpdateText(output, constants.GithubMinikubeReleasesURL, lastUpdateCheckFilePath)
|
MaybePrintUpdateText(constants.GithubMinikubeReleasesURL, lastUpdateCheckFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaybePrintUpdateText prints update text if needed
|
// MaybePrintUpdateText prints update text if needed
|
||||||
func MaybePrintUpdateText(output io.Writer, url string, lastUpdatePath string) {
|
func MaybePrintUpdateText(url string, lastUpdatePath string) {
|
||||||
if !shouldCheckURLVersion(lastUpdatePath) {
|
if !shouldCheckURLVersion(lastUpdatePath) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ func MaybePrintUpdateText(output io.Writer, url string, lastUpdatePath string) {
|
||||||
if err := writeTimeToFile(lastUpdateCheckFilePath, time.Now().UTC()); err != nil {
|
if err := writeTimeToFile(lastUpdateCheckFilePath, time.Now().UTC()); err != nil {
|
||||||
glog.Errorf("write time failed: %v", err)
|
glog.Errorf("write time failed: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(output, `There is a newer version of minikube available (%s%s). Download it here:
|
console.ErrStyle(console.WarningType, `There is a newer version of minikube available (%s%s). Download it here:
|
||||||
%s%s
|
%s%s
|
||||||
|
|
||||||
To disable this notification, run the following:
|
To disable this notification, run the following:
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
package notify
|
package notify
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -30,6 +29,7 @@ import (
|
||||||
"github.com/blang/semver"
|
"github.com/blang/semver"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"k8s.io/minikube/pkg/minikube/config"
|
"k8s.io/minikube/pkg/minikube/config"
|
||||||
|
"k8s.io/minikube/pkg/minikube/console"
|
||||||
"k8s.io/minikube/pkg/minikube/tests"
|
"k8s.io/minikube/pkg/minikube/tests"
|
||||||
"k8s.io/minikube/pkg/version"
|
"k8s.io/minikube/pkg/version"
|
||||||
)
|
)
|
||||||
|
@ -150,7 +150,8 @@ func TestMaybePrintUpdateText(t *testing.T) {
|
||||||
viper.Set(config.WantUpdateNotification, true)
|
viper.Set(config.WantUpdateNotification, true)
|
||||||
viper.Set(config.ReminderWaitPeriodInHours, 24)
|
viper.Set(config.ReminderWaitPeriodInHours, 24)
|
||||||
|
|
||||||
var outputBuffer bytes.Buffer
|
outputBuffer := tests.NewFakeFile()
|
||||||
|
console.SetErrFile(outputBuffer)
|
||||||
lastUpdateCheckFilePath := filepath.Join(tempDir, "last_update_check")
|
lastUpdateCheckFilePath := filepath.Join(tempDir, "last_update_check")
|
||||||
|
|
||||||
// test that no update text is printed if the latest version is lower/equal to the current version
|
// test that no update text is printed if the latest version is lower/equal to the current version
|
||||||
|
@ -161,7 +162,7 @@ func TestMaybePrintUpdateText(t *testing.T) {
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
MaybePrintUpdateText(&outputBuffer, server.URL, lastUpdateCheckFilePath)
|
MaybePrintUpdateText(server.URL, lastUpdateCheckFilePath)
|
||||||
if len(outputBuffer.String()) != 0 {
|
if len(outputBuffer.String()) != 0 {
|
||||||
t.Fatalf("Expected MaybePrintUpdateText to not output text as the current version is %s and version %s was served from URL but output was [%s]",
|
t.Fatalf("Expected MaybePrintUpdateText to not output text as the current version is %s and version %s was served from URL but output was [%s]",
|
||||||
version.GetVersion(), latestVersionFromURL, outputBuffer.String())
|
version.GetVersion(), latestVersionFromURL, outputBuffer.String())
|
||||||
|
@ -175,7 +176,7 @@ func TestMaybePrintUpdateText(t *testing.T) {
|
||||||
server = httptest.NewServer(handler)
|
server = httptest.NewServer(handler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
MaybePrintUpdateText(&outputBuffer, server.URL, lastUpdateCheckFilePath)
|
MaybePrintUpdateText(server.URL, lastUpdateCheckFilePath)
|
||||||
if len(outputBuffer.String()) == 0 {
|
if len(outputBuffer.String()) == 0 {
|
||||||
t.Fatalf("Expected MaybePrintUpdateText to output text as the current version is %s and version %s was served from URL but output was [%s]",
|
t.Fatalf("Expected MaybePrintUpdateText to output text as the current version is %s and version %s was served from URL but output was [%s]",
|
||||||
version.GetVersion(), latestVersionFromURL, outputBuffer.String())
|
version.GetVersion(), latestVersionFromURL, outputBuffer.String())
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"k8s.io/minikube/pkg/minikube/console"
|
"k8s.io/minikube/pkg/minikube/console"
|
||||||
|
"k8s.io/minikube/pkg/minikube/translate"
|
||||||
)
|
)
|
||||||
|
|
||||||
const issueBase = "https://github.com/kubernetes/minikube/issues"
|
const issueBase = "https://github.com/kubernetes/minikube/issues"
|
||||||
|
@ -52,7 +53,7 @@ type match struct {
|
||||||
// Display problem metadata to the console
|
// Display problem metadata to the console
|
||||||
func (p *Problem) Display() {
|
func (p *Problem) Display() {
|
||||||
console.ErrStyle(console.FailureType, "Error: [%s] %v", p.ID, p.Err)
|
console.ErrStyle(console.FailureType, "Error: [%s] %v", p.ID, p.Err)
|
||||||
console.ErrStyle(console.Tip, "Advice: %s", p.Advice)
|
console.ErrStyle(console.Tip, "Advice: %s", translate.T(p.Advice))
|
||||||
if p.URL != "" {
|
if p.URL != "" {
|
||||||
console.ErrStyle(console.Documentation, "Documentation: %s", p.URL)
|
console.ErrStyle(console.Documentation, "Documentation: %s", p.URL)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -43,3 +44,23 @@ func MakeTempDir() string {
|
||||||
os.Setenv(constants.MinikubeHome, tempDir)
|
os.Setenv(constants.MinikubeHome, tempDir)
|
||||||
return constants.GetMinipath()
|
return constants.GetMinipath()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FakeFile satisfies fdWriter
|
||||||
|
type FakeFile struct {
|
||||||
|
b bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFakeFile() *FakeFile {
|
||||||
|
return &FakeFile{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeFile) Fd() uintptr {
|
||||||
|
return uintptr(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeFile) Write(p []byte) (int, error) {
|
||||||
|
return f.b.Write(p)
|
||||||
|
}
|
||||||
|
func (f *FakeFile) String() string {
|
||||||
|
return f.b.String()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package translate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cloudfoundry-attic/jibber_jabber"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// preferredLanguage is the default language messages will be output in
|
||||||
|
preferredLanguage = language.AmericanEnglish
|
||||||
|
// our default language
|
||||||
|
defaultLanguage = language.AmericanEnglish
|
||||||
|
|
||||||
|
// Translations is a translation map from strings that can be output to console
|
||||||
|
// to its translation in the user's system locale.
|
||||||
|
Translations map[string]interface{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// T translates the given string to the supplied language.
|
||||||
|
func T(s string) string {
|
||||||
|
if preferredLanguage == defaultLanguage {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(Translations) == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := Translations[s]; ok {
|
||||||
|
if len(t.(string)) > 0 && t.(string) != " " {
|
||||||
|
return t.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetermineLocale finds the system locale and sets the preferred language for output appropriately.
|
||||||
|
func DetermineLocale() {
|
||||||
|
locale, err := jibber_jabber.DetectIETF()
|
||||||
|
if err != nil {
|
||||||
|
glog.Warningf("Getting system locale failed: %s", err)
|
||||||
|
locale = ""
|
||||||
|
}
|
||||||
|
err = SetPreferredLanguage(locale)
|
||||||
|
if err != nil {
|
||||||
|
glog.Warningf("Setting locale failed: %s", err)
|
||||||
|
preferredLanguage = defaultLanguage
|
||||||
|
}
|
||||||
|
|
||||||
|
if preferredLanguage == defaultLanguage {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load translations for preferred language into memory.
|
||||||
|
translationFile := "pkg/minikube/translate/translations/" + preferredLanguage.String() + ".json"
|
||||||
|
t, err := ioutil.ReadFile(translationFile)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Failed to load transalation file for %s: %s", preferredLanguage.String(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(t, &Translations)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Failed to populate translation map: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPreferredLanguageTag configures which language future messages should use.
|
||||||
|
func setPreferredLanguageTag(l language.Tag) {
|
||||||
|
glog.Infof("Setting Language to %s ...", l)
|
||||||
|
preferredLanguage = l
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPreferredLanguage configures which language future messages should use, based on a LANG string.
|
||||||
|
func SetPreferredLanguage(s string) error {
|
||||||
|
// "C" is commonly used to denote a neutral POSIX locale. See http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02
|
||||||
|
if s == "" || s == "C" {
|
||||||
|
setPreferredLanguageTag(defaultLanguage)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Handles "de_DE" or "de_DE.utf8"
|
||||||
|
// We don't process encodings, since Rob Pike invented utf8 and we're mostly stuck with it.
|
||||||
|
parts := strings.Split(s, ".")
|
||||||
|
l, err := language.Parse(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setPreferredLanguageTag(l)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreferredLanguage returns the preferred language tag.
|
||||||
|
func GetPreferredLanguage() language.Tag {
|
||||||
|
return preferredLanguage
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
{
|
||||||
|
"%q VM does not exist, nothing to stop": "",
|
||||||
|
"%q cluster does not exist": "",
|
||||||
|
"%q host does not exist, unable to show an IP": "",
|
||||||
|
"%q profile does not exist": "",
|
||||||
|
"%q stopped.": "",
|
||||||
|
"%s IP has been updated to point at %s": "",
|
||||||
|
"%s IP was already correctly configured for %s": "",
|
||||||
|
"%s has no available configuration options": "",
|
||||||
|
"%s is not responding properly: %v": "",
|
||||||
|
"%s is not yet a supported filesystem. We will try anyways!": "",
|
||||||
|
"%s was successfully configured": "",
|
||||||
|
"%s was successfully disabled": "",
|
||||||
|
"%s was successfully enabled": "",
|
||||||
|
"%s:%s is not running: %v": "",
|
||||||
|
"'none' driver does not support 'minikube docker-env' command": "",
|
||||||
|
"'none' driver does not support 'minikube mount' command": "",
|
||||||
|
"'none' driver does not support 'minikube ssh' command": "",
|
||||||
|
"Advice: %s": "",
|
||||||
|
"Alternatively, you may delete the existing VM using `minikube delete -p %s`": "",
|
||||||
|
"Cannot find directory %s for mount": "",
|
||||||
|
"Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "",
|
||||||
|
"Configuring environment for Kubernetes %s on %s %s": "Configurant l'environment pour Kubernetes %s sur %s %s",
|
||||||
|
"Configuring local host environment ...": "",
|
||||||
|
"Creating %s VM (CPUs=%d, Memory=%dMB, Disk=%dMB) ...": "Créant un VM %s (CPUs=%d, Mémoire=%dMB, Disque=%dMB)",
|
||||||
|
"Creating mount %s ...": "",
|
||||||
|
"Deleting %q from %s ...": "",
|
||||||
|
"Documentation: %s": "",
|
||||||
|
"Done! kubectl is now configured to use %q": "Fini! kubectl est maintenant configuré pour utiliser %s.",
|
||||||
|
"Download complete!": "",
|
||||||
|
"Downloading %s %s": "",
|
||||||
|
"Downloading Minikube ISO ...": "",
|
||||||
|
"ERROR creating `registry-creds-dpr` secret": "",
|
||||||
|
"ERROR creating `registry-creds-ecr` secret: %v": "",
|
||||||
|
"ERROR creating `registry-creds-gcr` secret: %v": "",
|
||||||
|
"Enabling dashboard ...": "",
|
||||||
|
"Error creating list template": "",
|
||||||
|
"Error creating minikube directory": "",
|
||||||
|
"Error creating status template": "",
|
||||||
|
"Error creating view template": "",
|
||||||
|
"Error executing list template": "",
|
||||||
|
"Error executing status template": "",
|
||||||
|
"Error executing template": "",
|
||||||
|
"Error executing view template": "",
|
||||||
|
"Error finding port for mount": "",
|
||||||
|
"Error getting IP": "",
|
||||||
|
"Error getting bootstrapper": "",
|
||||||
|
"Error getting client": "",
|
||||||
|
"Error getting client: %v": "",
|
||||||
|
"Error getting cluster": "",
|
||||||
|
"Error getting cluster bootstrapper": "",
|
||||||
|
"Error getting config": "",
|
||||||
|
"Error getting host": "",
|
||||||
|
"Error getting host status": "",
|
||||||
|
"Error getting machine logs": "",
|
||||||
|
"Error getting machine status": "",
|
||||||
|
"Error getting service status": "",
|
||||||
|
"Error getting service with namespace: %s and labels %s:%s: %v": "",
|
||||||
|
"Error getting the host IP address to use from within the VM": "",
|
||||||
|
"Error host driver ip status": "",
|
||||||
|
"Error killing mount process": "",
|
||||||
|
"Error loading api": "",
|
||||||
|
"Error opening service": "",
|
||||||
|
"Error reading %s: %v": "",
|
||||||
|
"Error restarting cluster": "",
|
||||||
|
"Error setting shell variables": "",
|
||||||
|
"Error starting cluster": "",
|
||||||
|
"Error starting mount": "",
|
||||||
|
"Error unsetting shell variables": "",
|
||||||
|
"Error writing mount pid": "",
|
||||||
|
"Error: [%s] %v": "",
|
||||||
|
"Exiting due to %s signal": "",
|
||||||
|
"Failed to cache ISO": "",
|
||||||
|
"Failed to cache and load images": "",
|
||||||
|
"Failed to cache binaries": "",
|
||||||
|
"Failed to cache images": "",
|
||||||
|
"Failed to check if machine exists": "",
|
||||||
|
"Failed to check main repository and mirrors for images for images": "",
|
||||||
|
"Failed to chown %s: %v": "",
|
||||||
|
"Failed to delete cluster": "",
|
||||||
|
"Failed to delete images": "",
|
||||||
|
"Failed to delete images from config": "",
|
||||||
|
"Failed to download kubectl": "",
|
||||||
|
"Failed to enable container runtime": "",
|
||||||
|
"Failed to generate config": "",
|
||||||
|
"Failed to get bootstrapper": "",
|
||||||
|
"Failed to get command runner": "",
|
||||||
|
"Failed to get driver URL": "",
|
||||||
|
"Failed to get image map": "",
|
||||||
|
"Failed to get machine client": "",
|
||||||
|
"Failed to get service URL: %v": "",
|
||||||
|
"Failed to kill mount process: %v": "",
|
||||||
|
"Failed to list cached images": "",
|
||||||
|
"Failed to remove profile": "",
|
||||||
|
"Failed to save config": "",
|
||||||
|
"Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,%s`.": "",
|
||||||
|
"Failed to setup certs": "",
|
||||||
|
"Failed to setup kubeconfig": "",
|
||||||
|
"Failed to update cluster": "",
|
||||||
|
"Failed to update config": "",
|
||||||
|
"Failed unmount: %v": "",
|
||||||
|
"Follow": "",
|
||||||
|
"For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "",
|
||||||
|
"For more information, see:": "",
|
||||||
|
"Found network options:": "",
|
||||||
|
"GID: %s": "",
|
||||||
|
"If the above advice does not help, please let us know: ": "",
|
||||||
|
"Ignoring --vm-driver=%s, as the existing %q VM was created using the %s driver.": "",
|
||||||
|
"IsEnabled failed": "",
|
||||||
|
"Kubernetes downgrade is not supported, will continue to use %v": "",
|
||||||
|
"Launching Kubernetes ... ": "Lançant Kubernetes ...",
|
||||||
|
"Launching proxy ...": "",
|
||||||
|
"MSize: %d": "",
|
||||||
|
"Mode: %o (%s)": "",
|
||||||
|
"Mount options:": "",
|
||||||
|
"Mounting host path %s into VM as %s ...": "",
|
||||||
|
"NOTE: This process must stay alive for the mount to be accessible ...": "",
|
||||||
|
"None of known repositories in your location is accessible. Use %s as fallback.": "",
|
||||||
|
"None of known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "",
|
||||||
|
"Opening %s in your default browser...": "",
|
||||||
|
"Opening kubernetes service %s/%s in default browser...": "",
|
||||||
|
"Options: %s": "",
|
||||||
|
"Please enter a value:": "",
|
||||||
|
"Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "",
|
||||||
|
"Powering off %q via SSH ...": "",
|
||||||
|
"Problems detected in %q:": "",
|
||||||
|
"Pulling images ...": "Extrayant les images ... ",
|
||||||
|
"Re-using the currently running %s VM for %q ...": "",
|
||||||
|
"Related issues:": "",
|
||||||
|
"Relaunching Kubernetes %s using %s ... ": "",
|
||||||
|
"Requested disk size (%dMB) is less than minimum of %dMB": "",
|
||||||
|
"Restarting existing %s VM for %q ...": "",
|
||||||
|
"Set failed": "",
|
||||||
|
"Setting profile failed": "",
|
||||||
|
"Skipped switching kubectl context for %s , because --keep-context": "",
|
||||||
|
"Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "",
|
||||||
|
"Sorry, completion support is not yet implemented for %q": "",
|
||||||
|
"Sorry, the --gpu feature is currently only supported with --vm-driver=kvm2": "",
|
||||||
|
"Sorry, the --hidden feature is currently only supported with --vm-driver=kvm2": "",
|
||||||
|
"Sorry, the kubeadm.%s parameter is currently not supported by --extra-config": "",
|
||||||
|
"Stopping %q in %s ...": "",
|
||||||
|
"Successfully mounted %s to %s": "",
|
||||||
|
"Target directory %q must be an absolute path": "",
|
||||||
|
"The %q cluster has been deleted.": "",
|
||||||
|
"The 'none' driver provides limited isolation and may reduce system security and reliability.": "",
|
||||||
|
"The docker host is currently not running": "",
|
||||||
|
"The docker service is currently not active": "",
|
||||||
|
"The kvm driver is deprecated and support for it will be removed in a future release.\n\t\t\t\tPlease consider switching to the kvm2 driver, which is intended to replace the kvm driver.\n\t\t\t\tSee https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver for more information.\n\t\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "",
|
||||||
|
"The value passed to --format is invalid": "",
|
||||||
|
"The value passed to --format is invalid: %s": "",
|
||||||
|
"The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\t\tSee https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#vmware-unified-driver for more information.\n\t\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "",
|
||||||
|
"The xhyve driver is deprecated and support for it will be removed in a future release.\nPlease consider switching to the hyperkit driver, which is intended to replace the xhyve driver.\nSee https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#hyperkit-driver for more information.\nTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "",
|
||||||
|
"There is a newer version of minikube available (%s%s). Download it here:\n%s%s\n\nTo disable this notification, run the following:\nminikube config set WantUpdateNotification false\n": "",
|
||||||
|
"These changes will take effect upon a minikube delete and then a minikube start": "",
|
||||||
|
"This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label %s:%s": "",
|
||||||
|
"This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "",
|
||||||
|
"Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "",
|
||||||
|
"To connect to this cluster, use: kubectl --context=%s": "",
|
||||||
|
"To switch drivers, you may create a new VM using `minikube start -p \u003cname\u003e --vm-driver=%s`": "",
|
||||||
|
"To use kubectl or minikube commands as your own user, you may": "",
|
||||||
|
"Type: %s": "",
|
||||||
|
"UID: %s": "",
|
||||||
|
"Unable to bind flags": "",
|
||||||
|
"Unable to enable dashboard": "",
|
||||||
|
"Unable to fetch latest version info": "",
|
||||||
|
"Unable to get VM IP address": "",
|
||||||
|
"Unable to get runtime": "",
|
||||||
|
"Unable to kill mount process: %s": "",
|
||||||
|
"Unable to load cached images from config file.": "",
|
||||||
|
"Unable to load cached images: %v": "",
|
||||||
|
"Unable to load config: %v": "",
|
||||||
|
"Unable to parse %q: %v": "",
|
||||||
|
"Unable to pull images, which may be OK: %v": "",
|
||||||
|
"Unable to start VM": "",
|
||||||
|
"Unable to stop VM": "",
|
||||||
|
"Uninstalling Kubernetes %s using %s ...": "",
|
||||||
|
"Unmounting %s ...": "",
|
||||||
|
"Update server returned an empty list": "",
|
||||||
|
"Usage: minikube completion SHELL": "",
|
||||||
|
"Userspace file server is shutdown": "",
|
||||||
|
"Userspace file server: ": "",
|
||||||
|
"Verifying dashboard health ...": "",
|
||||||
|
"Verifying proxy health ...": "",
|
||||||
|
"Verifying:": "Vérifiant:",
|
||||||
|
"Version: %s": "",
|
||||||
|
"Wait failed": "",
|
||||||
|
"Wait failed: %v": "",
|
||||||
|
"Waiting for SSH access ...": "Attendant l'accès SSH ...",
|
||||||
|
"You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP (%s). Please see https://github.com/kubernetes/minikube/blob/master/docs/http_proxy.md for more details": "",
|
||||||
|
"You must specify a service name": "",
|
||||||
|
"addon '%s' is currently not enabled.\nTo enable this addon run:\nminikube addons enable %s": "",
|
||||||
|
"addon '%s' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "",
|
||||||
|
"addon list failed": "",
|
||||||
|
"api load": "",
|
||||||
|
"bash completion failed": "",
|
||||||
|
"checking main repository and mirrors for images": "",
|
||||||
|
"command runner": "",
|
||||||
|
"config view failed": "",
|
||||||
|
"disable failed": "",
|
||||||
|
"enable failed: %v": "",
|
||||||
|
"env %s": "",
|
||||||
|
"error creating clientset": "",
|
||||||
|
"error creating machine client": "",
|
||||||
|
"error getting driver": "",
|
||||||
|
"error parsing the input ip address for mount": "",
|
||||||
|
"error starting tunnel": "",
|
||||||
|
"failed to open browser: %v": "",
|
||||||
|
"kubectl and minikube configuration will be stored in %s": "",
|
||||||
|
"kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "",
|
||||||
|
"kubectl proxy": "",
|
||||||
|
"logdir set failed": "",
|
||||||
|
"minikube %s on %s (%s)": "minikube %s sur %s (%s)",
|
||||||
|
"minikube is not running, so the service cannot be accessed": "",
|
||||||
|
"minikube profile was successfully set to %s": "",
|
||||||
|
"minikube will upgrade the local cluster from Kubernetes %s to %s": "",
|
||||||
|
"mount argument %q must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "",
|
||||||
|
"mount failed": "",
|
||||||
|
"need to relocate them. For example, to overwrite your own settings:": "",
|
||||||
|
"opt %s": "",
|
||||||
|
"stat failed": "",
|
||||||
|
"unable to bind flags": "",
|
||||||
|
"unable to set logtostderr": "",
|
||||||
|
"unset failed": "",
|
||||||
|
"unsupported driver: %s": "",
|
||||||
|
"update config": "",
|
||||||
|
"usage: minikube addons configure ADDON_NAME": "",
|
||||||
|
"usage: minikube addons disable ADDON_NAME": "",
|
||||||
|
"usage: minikube addons enable ADDON_NAME": "",
|
||||||
|
"usage: minikube addons list": "",
|
||||||
|
"usage: minikube addons open ADDON_NAME": "",
|
||||||
|
"usage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "",
|
||||||
|
"usage: minikube config unset PROPERTY_NAME": "",
|
||||||
|
"usage: minikube delete": "",
|
||||||
|
"usage: minikube profile [MINIKUBE_PROFILE_NAME]": "",
|
||||||
|
"using image repository %s": "",
|
||||||
|
"zsh completion failed": ""
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
{
|
||||||
|
"%q VM does not exist, nothing to stop": "",
|
||||||
|
"%q cluster does not exist": "",
|
||||||
|
"%q host does not exist, unable to show an IP": "",
|
||||||
|
"%q profile does not exist": "",
|
||||||
|
"%q stopped.": "",
|
||||||
|
"%s IP has been updated to point at %s": "",
|
||||||
|
"%s IP was already correctly configured for %s": "",
|
||||||
|
"%s has no available configuration options": "",
|
||||||
|
"%s is not responding properly: %v": "",
|
||||||
|
"%s is not yet a supported filesystem. We will try anyways!": "",
|
||||||
|
"%s was successfully configured": "",
|
||||||
|
"%s was successfully disabled": "",
|
||||||
|
"%s was successfully enabled": "",
|
||||||
|
"%s:%s is not running: %v": "",
|
||||||
|
"'none' driver does not support 'minikube docker-env' command": "",
|
||||||
|
"'none' driver does not support 'minikube mount' command": "",
|
||||||
|
"'none' driver does not support 'minikube ssh' command": "",
|
||||||
|
"Advice: %s": "",
|
||||||
|
"Alternatively, you may delete the existing VM using `minikube delete -p %s`": "",
|
||||||
|
"Cannot find directory %s for mount": "",
|
||||||
|
"Check that minikube is running and that you have specified the correct namespace (-n flag) if required.": "",
|
||||||
|
"Configuring environment for Kubernetes %s on %s %s": "开始为Kubernetes %s,%s %s 配置环境变量",
|
||||||
|
"Configuring local host environment ...": "",
|
||||||
|
"Creating %s VM (CPUs=%d, Memory=%dMB, Disk=%dMB) ...": "正在创建%s虚拟机(CPU =%d,内存=%dMB,磁盘=%dMB)...",
|
||||||
|
"Creating mount %s ...": "",
|
||||||
|
"Deleting %q from %s ...": "",
|
||||||
|
"Documentation: %s": "",
|
||||||
|
"Done! kubectl is now configured to use %q": "完成!kubectl已经配置至%q",
|
||||||
|
"Download complete!": "",
|
||||||
|
"Downloading %s %s": "",
|
||||||
|
"Downloading Minikube ISO ...": "",
|
||||||
|
"ERROR creating `registry-creds-dpr` secret": "",
|
||||||
|
"ERROR creating `registry-creds-ecr` secret: %v": "",
|
||||||
|
"ERROR creating `registry-creds-gcr` secret: %v": "",
|
||||||
|
"Enabling dashboard ...": "",
|
||||||
|
"Error creating list template": "",
|
||||||
|
"Error creating minikube directory": "",
|
||||||
|
"Error creating status template": "",
|
||||||
|
"Error creating view template": "",
|
||||||
|
"Error executing list template": "",
|
||||||
|
"Error executing status template": "",
|
||||||
|
"Error executing template": "",
|
||||||
|
"Error executing view template": "",
|
||||||
|
"Error finding port for mount": "",
|
||||||
|
"Error getting IP": "",
|
||||||
|
"Error getting bootstrapper": "",
|
||||||
|
"Error getting client": "",
|
||||||
|
"Error getting client: %v": "",
|
||||||
|
"Error getting cluster": "",
|
||||||
|
"Error getting cluster bootstrapper": "",
|
||||||
|
"Error getting config": "",
|
||||||
|
"Error getting host": "",
|
||||||
|
"Error getting host status": "",
|
||||||
|
"Error getting machine logs": "",
|
||||||
|
"Error getting machine status": "",
|
||||||
|
"Error getting service status": "",
|
||||||
|
"Error getting service with namespace: %s and labels %s:%s: %v": "",
|
||||||
|
"Error getting the host IP address to use from within the VM": "",
|
||||||
|
"Error host driver ip status": "",
|
||||||
|
"Error killing mount process": "",
|
||||||
|
"Error loading api": "",
|
||||||
|
"Error opening service": "",
|
||||||
|
"Error reading %s: %v": "",
|
||||||
|
"Error restarting cluster": "",
|
||||||
|
"Error setting shell variables": "",
|
||||||
|
"Error starting cluster": "",
|
||||||
|
"Error starting mount": "",
|
||||||
|
"Error unsetting shell variables": "",
|
||||||
|
"Error writing mount pid": "",
|
||||||
|
"Error: [%s] %v": "",
|
||||||
|
"Exiting due to %s signal": "",
|
||||||
|
"Failed to cache ISO": "",
|
||||||
|
"Failed to cache and load images": "",
|
||||||
|
"Failed to cache binaries": "",
|
||||||
|
"Failed to cache images": "",
|
||||||
|
"Failed to check if machine exists": "",
|
||||||
|
"Failed to check main repository and mirrors for images for images": "",
|
||||||
|
"Failed to chown %s: %v": "",
|
||||||
|
"Failed to delete cluster": "",
|
||||||
|
"Failed to delete images": "",
|
||||||
|
"Failed to delete images from config": "",
|
||||||
|
"Failed to download kubectl": "",
|
||||||
|
"Failed to enable container runtime": "",
|
||||||
|
"Failed to generate config": "",
|
||||||
|
"Failed to get bootstrapper": "",
|
||||||
|
"Failed to get command runner": "",
|
||||||
|
"Failed to get driver URL": "",
|
||||||
|
"Failed to get image map": "",
|
||||||
|
"Failed to get machine client": "",
|
||||||
|
"Failed to get service URL: %v": "",
|
||||||
|
"Failed to kill mount process: %v": "",
|
||||||
|
"Failed to list cached images": "",
|
||||||
|
"Failed to remove profile": "",
|
||||||
|
"Failed to save config": "",
|
||||||
|
"Failed to set NO_PROXY Env. Please use `export NO_PROXY=$NO_PROXY,%s`.": "",
|
||||||
|
"Failed to setup certs": "",
|
||||||
|
"Failed to setup kubeconfig": "",
|
||||||
|
"Failed to update cluster": "",
|
||||||
|
"Failed to update config": "",
|
||||||
|
"Failed unmount: %v": "",
|
||||||
|
"Follow": "",
|
||||||
|
"For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "",
|
||||||
|
"For more information, see:": "",
|
||||||
|
"Found network options:": "",
|
||||||
|
"GID: %s": "",
|
||||||
|
"If the above advice does not help, please let us know: ": "",
|
||||||
|
"Ignoring --vm-driver=%s, as the existing %q VM was created using the %s driver.": "",
|
||||||
|
"IsEnabled failed": "",
|
||||||
|
"Kubernetes downgrade is not supported, will continue to use %v": "",
|
||||||
|
"Launching Kubernetes ... ": "正在启动 Kubernetes ... ",
|
||||||
|
"Launching proxy ...": "",
|
||||||
|
"MSize: %d": "",
|
||||||
|
"Mode: %o (%s)": "",
|
||||||
|
"Mount options:": "",
|
||||||
|
"Mounting host path %s into VM as %s ...": "",
|
||||||
|
"NOTE: This process must stay alive for the mount to be accessible ...": "",
|
||||||
|
"None of known repositories in your location is accessible. Use %s as fallback.": "",
|
||||||
|
"None of known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag": "",
|
||||||
|
"Opening %s in your default browser...": "",
|
||||||
|
"Opening kubernetes service %s/%s in default browser...": "",
|
||||||
|
"Options: %s": "",
|
||||||
|
"Please enter a value:": "",
|
||||||
|
"Please specify the directory to be mounted: \n\tminikube mount \u003csource directory\u003e:\u003ctarget directory\u003e (example: \"/host-home:/vm-home\")": "",
|
||||||
|
"Powering off %q via SSH ...": "",
|
||||||
|
"Problems detected in %q:": "",
|
||||||
|
"Pulling images ...": "拉取镜像 ...",
|
||||||
|
"Re-using the currently running %s VM for %q ...": "",
|
||||||
|
"Related issues:": "",
|
||||||
|
"Relaunching Kubernetes %s using %s ... ": "",
|
||||||
|
"Requested disk size (%dMB) is less than minimum of %dMB": "",
|
||||||
|
"Restarting existing %s VM for %q ...": "",
|
||||||
|
"Set failed": "",
|
||||||
|
"Setting profile failed": "",
|
||||||
|
"Skipped switching kubectl context for %s , because --keep-context": "",
|
||||||
|
"Sorry that minikube crashed. If this was unexpected, we would love to hear from you:": "",
|
||||||
|
"Sorry, completion support is not yet implemented for %q": "",
|
||||||
|
"Sorry, the --gpu feature is currently only supported with --vm-driver=kvm2": "",
|
||||||
|
"Sorry, the --hidden feature is currently only supported with --vm-driver=kvm2": "",
|
||||||
|
"Sorry, the kubeadm.%s parameter is currently not supported by --extra-config": "",
|
||||||
|
"Stopping %q in %s ...": "",
|
||||||
|
"Successfully mounted %s to %s": "",
|
||||||
|
"Target directory %q must be an absolute path": "",
|
||||||
|
"The %q cluster has been deleted.": "",
|
||||||
|
"The 'none' driver provides limited isolation and may reduce system security and reliability.": "",
|
||||||
|
"The docker host is currently not running": "",
|
||||||
|
"The docker service is currently not active": "",
|
||||||
|
"The kvm driver is deprecated and support for it will be removed in a future release.\n\t\t\t\tPlease consider switching to the kvm2 driver, which is intended to replace the kvm driver.\n\t\t\t\tSee https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver for more information.\n\t\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "",
|
||||||
|
"The value passed to --format is invalid": "",
|
||||||
|
"The value passed to --format is invalid: %s": "",
|
||||||
|
"The vmwarefusion driver is deprecated and support for it will be removed in a future release.\n\t\t\t\tPlease consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.\n\t\t\t\tSee https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#vmware-unified-driver for more information.\n\t\t\t\tTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "",
|
||||||
|
"The xhyve driver is deprecated and support for it will be removed in a future release.\nPlease consider switching to the hyperkit driver, which is intended to replace the xhyve driver.\nSee https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#hyperkit-driver for more information.\nTo disable this message, run [minikube config set ShowDriverDeprecationNotification false]": "",
|
||||||
|
"There is a newer version of minikube available (%s%s). Download it here:\n%s%s\n\nTo disable this notification, run the following:\nminikube config set WantUpdateNotification false\n": "",
|
||||||
|
"These changes will take effect upon a minikube delete and then a minikube start": "",
|
||||||
|
"This addon does not have an endpoint defined for the 'addons open' command.\nYou can add one by annotating a service with the label %s:%s": "",
|
||||||
|
"This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true": "",
|
||||||
|
"Tip: Use 'minikube start -p \u003cname\u003e' to create a new cluster, or 'minikube delete' to delete this one.": "",
|
||||||
|
"To connect to this cluster, use: kubectl --context=%s": "",
|
||||||
|
"To switch drivers, you may create a new VM using `minikube start -p \u003cname\u003e --vm-driver=%s`": "",
|
||||||
|
"To use kubectl or minikube commands as your own user, you may": "",
|
||||||
|
"Type: %s": "",
|
||||||
|
"UID: %s": "",
|
||||||
|
"Unable to bind flags": "",
|
||||||
|
"Unable to enable dashboard": "",
|
||||||
|
"Unable to fetch latest version info": "",
|
||||||
|
"Unable to get VM IP address": "",
|
||||||
|
"Unable to get runtime": "",
|
||||||
|
"Unable to kill mount process: %s": "",
|
||||||
|
"Unable to load cached images from config file.": "",
|
||||||
|
"Unable to load cached images: %v": "",
|
||||||
|
"Unable to load config: %v": "",
|
||||||
|
"Unable to parse %q: %v": "",
|
||||||
|
"Unable to pull images, which may be OK: %v": "",
|
||||||
|
"Unable to start VM": "",
|
||||||
|
"Unable to stop VM": "",
|
||||||
|
"Uninstalling Kubernetes %s using %s ...": "",
|
||||||
|
"Unmounting %s ...": "",
|
||||||
|
"Update server returned an empty list": "",
|
||||||
|
"Usage: minikube completion SHELL": "",
|
||||||
|
"Userspace file server is shutdown": "",
|
||||||
|
"Userspace file server: ": "",
|
||||||
|
"Verifying dashboard health ...": "",
|
||||||
|
"Verifying proxy health ...": "",
|
||||||
|
"Verifying:": "正在验证:",
|
||||||
|
"Version: %s": "",
|
||||||
|
"Wait failed": "",
|
||||||
|
"Wait failed: %v": "",
|
||||||
|
"Waiting for SSH access ...": "",
|
||||||
|
"You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP (%s). Please see https://github.com/kubernetes/minikube/blob/master/docs/http_proxy.md for more details": "",
|
||||||
|
"You must specify a service name": "",
|
||||||
|
"addon '%s' is currently not enabled.\nTo enable this addon run:\nminikube addons enable %s": "",
|
||||||
|
"addon '%s' is not a valid addon packaged with minikube.\nTo see the list of available addons run:\nminikube addons list": "",
|
||||||
|
"addon list failed": "",
|
||||||
|
"api load": "",
|
||||||
|
"bash completion failed": "",
|
||||||
|
"checking main repository and mirrors for images": "",
|
||||||
|
"command runner": "",
|
||||||
|
"config view failed": "",
|
||||||
|
"disable failed": "",
|
||||||
|
"enable failed: %v": "",
|
||||||
|
"env %s": "",
|
||||||
|
"error creating clientset": "",
|
||||||
|
"error creating machine client": "",
|
||||||
|
"error getting driver": "",
|
||||||
|
"error parsing the input ip address for mount": "",
|
||||||
|
"error starting tunnel": "",
|
||||||
|
"failed to open browser: %v": "",
|
||||||
|
"kubectl and minikube configuration will be stored in %s": "",
|
||||||
|
"kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/": "",
|
||||||
|
"kubectl proxy": "",
|
||||||
|
"logdir set failed": "",
|
||||||
|
"minikube %s on %s (%s)": "您正在使用minikube %s, 运行平台:%s (%s)",
|
||||||
|
"minikube is not running, so the service cannot be accessed": "",
|
||||||
|
"minikube profile was successfully set to %s": "",
|
||||||
|
"minikube will upgrade the local cluster from Kubernetes %s to %s": "",
|
||||||
|
"mount argument %q must be in form: \u003csource directory\u003e:\u003ctarget directory\u003e": "",
|
||||||
|
"mount failed": "",
|
||||||
|
"need to relocate them. For example, to overwrite your own settings:": "",
|
||||||
|
"opt %s": "",
|
||||||
|
"stat failed": "",
|
||||||
|
"unable to bind flags": "",
|
||||||
|
"unable to set logtostderr": "",
|
||||||
|
"unset failed": "",
|
||||||
|
"unsupported driver: %s": "",
|
||||||
|
"update config": "",
|
||||||
|
"usage: minikube addons configure ADDON_NAME": "",
|
||||||
|
"usage: minikube addons disable ADDON_NAME": "",
|
||||||
|
"usage: minikube addons enable ADDON_NAME": "",
|
||||||
|
"usage: minikube addons list": "",
|
||||||
|
"usage: minikube addons open ADDON_NAME": "",
|
||||||
|
"usage: minikube config set PROPERTY_NAME PROPERTY_VALUE": "",
|
||||||
|
"usage: minikube config unset PROPERTY_NAME": "",
|
||||||
|
"usage: minikube delete": "",
|
||||||
|
"usage: minikube profile [MINIKUBE_PROFILE_NAME]": "",
|
||||||
|
"using image repository %s": "",
|
||||||
|
"zsh completion failed": ""
|
||||||
|
}
|
Loading…
Reference in New Issue