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 {
|
||||
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.
|
||||
err = proxy.ExcludeIP(ip)
|
||||
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
|
||||
config.KubernetesConfig.NodeIP = ip
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"k8s.io/minikube/pkg/minikube/console"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/minikube/machine"
|
||||
"k8s.io/minikube/pkg/minikube/translate"
|
||||
_ "k8s.io/minikube/pkg/provision"
|
||||
)
|
||||
|
||||
|
@ -40,6 +41,6 @@ func main() {
|
|||
}
|
||||
console.SetOutFile(os.Stdout)
|
||||
console.SetErrFile(os.Stderr)
|
||||
console.DetermineLocale()
|
||||
translate.DetermineLocale()
|
||||
cmd.Execute()
|
||||
}
|
||||
|
|
7
go.mod
7
go.mod
|
@ -19,6 +19,7 @@ require (
|
|||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680 // 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/groupcache v0.0.0-20160516000752-02826c3e7903 // 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/gojsonschema v0.0.0-20160623135812-c539bca196be
|
||||
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/sync v0.0.0-20190227155943-e225da77a7e6
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d // 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=
|
||||
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/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/go.mod h1:HCOMm3Hulq/xuEVQMyZOuQlA+dSZpFY5kdCTZWjMVis=
|
||||
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/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/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/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
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/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/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/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
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=
|
||||
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-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-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/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/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-20190115181402-5dab4167f31c h1:pcBdqVcrlT+A3i+tWsOROFONQyey9tisIQHI4xqVGLg=
|
||||
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-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-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-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/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-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.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
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/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-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.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
|
|
@ -24,11 +24,10 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudfoundry-attic/jibber_jabber"
|
||||
"github.com/golang/glog"
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
"golang.org/x/text/language"
|
||||
"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
|
||||
|
@ -48,10 +47,6 @@ var (
|
|||
outFile fdWriter
|
||||
// errFile is where Err* functions send output to. Set using SetErrFile()
|
||||
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 = false
|
||||
// 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
|
||||
func Out(format string, a ...interface{}) {
|
||||
p := message.NewPrinter(preferredLanguage)
|
||||
p := message.NewPrinter(translate.GetPreferredLanguage())
|
||||
if outFile == nil {
|
||||
glog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...))
|
||||
return
|
||||
|
@ -104,7 +99,7 @@ func ErrStyle(style StyleEnum, format string, a ...interface{}) {
|
|||
|
||||
// Err writes a basic formatted string to stderr
|
||||
func Err(format string, a ...interface{}) {
|
||||
p := message.NewPrinter(preferredLanguage)
|
||||
p := message.NewPrinter(translate.GetPreferredLanguage())
|
||||
if errFile == nil {
|
||||
glog.Errorf("[unset errFile]: %s", fmt.Sprintf(format, a...))
|
||||
return
|
||||
|
@ -140,30 +135,6 @@ func Failure(format string, a ...interface{}) {
|
|||
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.
|
||||
func SetOutFile(w fdWriter) {
|
||||
glog.Infof("Setting OutFile to fd %d ...", w.Fd())
|
||||
|
@ -178,17 +149,6 @@ func SetErrFile(w fdWriter) {
|
|||
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.
|
||||
func wantsColor(fd uintptr) bool {
|
||||
// First process the environment: we allow users to force colors on or off.
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package console
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
@ -25,31 +24,13 @@ import (
|
|||
|
||||
"golang.org/x/text/language"
|
||||
"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) {
|
||||
|
||||
var tests = []struct {
|
||||
var testCases = []struct {
|
||||
style StyleEnum
|
||||
message string
|
||||
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"},
|
||||
{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} {
|
||||
t.Run(fmt.Sprintf("%s-override-%v", tc.message, override), func(t *testing.T) {
|
||||
// Set MINIKUBE_IN_STYLE=<override>
|
||||
os.Setenv(OverrideEnv, strconv.FormatBool(override))
|
||||
f := newFakeFile()
|
||||
f := tests.NewFakeFile()
|
||||
SetOutFile(f)
|
||||
OutStyle(tc.style, tc.message, tc.params...)
|
||||
got := f.String()
|
||||
|
@ -93,21 +74,23 @@ func TestOut(t *testing.T) {
|
|||
t.Fatalf("setstring: %v", err)
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
var testCases = []struct {
|
||||
format string
|
||||
lang language.Tag
|
||||
lang string
|
||||
arg interface{}
|
||||
want string
|
||||
}{
|
||||
{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: language.AmericanEnglish, arg: "v1.13", want: "Installing Kubernetes version v1.13 ..."},
|
||||
{format: "Installing Kubernetes version %s ...", lang: "ar", arg: "v1.13", want: "... v1.13 تثبيت Kubernetes الإصدار"},
|
||||
{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"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.format, func(t *testing.T) {
|
||||
SetPreferredLanguageTag(tc.lang)
|
||||
f := newFakeFile()
|
||||
if err := translate.SetPreferredLanguage(tc.lang); err != nil {
|
||||
t.Errorf("unexpected error: %q", err)
|
||||
}
|
||||
f := tests.NewFakeFile()
|
||||
SetOutFile(f)
|
||||
ErrLn("unrelated message")
|
||||
Out(tc.format, tc.arg)
|
||||
|
@ -121,7 +104,7 @@ func TestOut(t *testing.T) {
|
|||
|
||||
func TestErr(t *testing.T) {
|
||||
os.Setenv(OverrideEnv, "0")
|
||||
f := newFakeFile()
|
||||
f := tests.NewFakeFile()
|
||||
SetErrFile(f)
|
||||
Err("xyz123 %s\n", "%s%%%d")
|
||||
OutLn("unrelated message")
|
||||
|
@ -135,7 +118,7 @@ func TestErr(t *testing.T) {
|
|||
|
||||
func TestErrStyle(t *testing.T) {
|
||||
os.Setenv(OverrideEnv, "1")
|
||||
f := newFakeFile()
|
||||
f := tests.NewFakeFile()
|
||||
SetErrFile(f)
|
||||
ErrStyle(FatalType, "error: %s", "%s%%%d")
|
||||
got := f.String()
|
||||
|
@ -159,14 +142,15 @@ func TestSetPreferredLanguage(t *testing.T) {
|
|||
for _, tc := range tests {
|
||||
t.Run(tc.input, func(t *testing.T) {
|
||||
// Set something so that we can assert change.
|
||||
SetPreferredLanguageTag(language.Icelandic)
|
||||
if err := SetPreferredLanguage(tc.input); err != nil {
|
||||
if err := translate.SetPreferredLanguage("is"); err != nil {
|
||||
t.Errorf("unexpected error: %q", err)
|
||||
}
|
||||
if err := translate.SetPreferredLanguage(tc.input); err != nil {
|
||||
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()
|
||||
got, _ := preferredLanguage.Base()
|
||||
got, _ := translate.GetPreferredLanguage().Base()
|
||||
if 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/number"
|
||||
"k8s.io/minikube/pkg/minikube/translate"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -131,12 +132,13 @@ func lowPrefix(s style) string {
|
|||
|
||||
// Apply styling to a format 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 {
|
||||
if _, ok := x.(int); ok {
|
||||
a[i] = number.Decimal(x, number.NoSeparator())
|
||||
}
|
||||
}
|
||||
format = translate.T(format)
|
||||
out := p.Sprintf(format, a...)
|
||||
|
||||
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
|
||||
glog.Warningf(fmt.Sprintf("%s: %v", msg, err))
|
||||
console.Err("\n")
|
||||
console.Fatal(msg+": %v", err)
|
||||
console.Fatal("%s: %v", msg, err)
|
||||
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.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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
@ -31,6 +30,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/console"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/version"
|
||||
)
|
||||
|
@ -43,12 +43,12 @@ var (
|
|||
)
|
||||
|
||||
// MaybePrintUpdateTextFromGithub prints update text if needed, from github
|
||||
func MaybePrintUpdateTextFromGithub(output io.Writer) {
|
||||
MaybePrintUpdateText(output, constants.GithubMinikubeReleasesURL, lastUpdateCheckFilePath)
|
||||
func MaybePrintUpdateTextFromGithub() {
|
||||
MaybePrintUpdateText(constants.GithubMinikubeReleasesURL, lastUpdateCheckFilePath)
|
||||
}
|
||||
|
||||
// MaybePrintUpdateText prints update text if needed
|
||||
func MaybePrintUpdateText(output io.Writer, url string, lastUpdatePath string) {
|
||||
func MaybePrintUpdateText(url string, lastUpdatePath string) {
|
||||
if !shouldCheckURLVersion(lastUpdatePath) {
|
||||
return
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func MaybePrintUpdateText(output io.Writer, url string, lastUpdatePath string) {
|
|||
if err := writeTimeToFile(lastUpdateCheckFilePath, time.Now().UTC()); err != nil {
|
||||
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
|
||||
|
||||
To disable this notification, run the following:
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package notify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -30,6 +29,7 @@ import (
|
|||
"github.com/blang/semver"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/console"
|
||||
"k8s.io/minikube/pkg/minikube/tests"
|
||||
"k8s.io/minikube/pkg/version"
|
||||
)
|
||||
|
@ -150,7 +150,8 @@ func TestMaybePrintUpdateText(t *testing.T) {
|
|||
viper.Set(config.WantUpdateNotification, true)
|
||||
viper.Set(config.ReminderWaitPeriodInHours, 24)
|
||||
|
||||
var outputBuffer bytes.Buffer
|
||||
outputBuffer := tests.NewFakeFile()
|
||||
console.SetErrFile(outputBuffer)
|
||||
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
|
||||
|
@ -161,7 +162,7 @@ func TestMaybePrintUpdateText(t *testing.T) {
|
|||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
MaybePrintUpdateText(&outputBuffer, server.URL, lastUpdateCheckFilePath)
|
||||
MaybePrintUpdateText(server.URL, lastUpdateCheckFilePath)
|
||||
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]",
|
||||
version.GetVersion(), latestVersionFromURL, outputBuffer.String())
|
||||
|
@ -175,7 +176,7 @@ func TestMaybePrintUpdateText(t *testing.T) {
|
|||
server = httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
MaybePrintUpdateText(&outputBuffer, server.URL, lastUpdateCheckFilePath)
|
||||
MaybePrintUpdateText(server.URL, lastUpdateCheckFilePath)
|
||||
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]",
|
||||
version.GetVersion(), latestVersionFromURL, outputBuffer.String())
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"regexp"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/console"
|
||||
"k8s.io/minikube/pkg/minikube/translate"
|
||||
)
|
||||
|
||||
const issueBase = "https://github.com/kubernetes/minikube/issues"
|
||||
|
@ -52,7 +53,7 @@ type match struct {
|
|||
// Display problem metadata to the console
|
||||
func (p *Problem) Display() {
|
||||
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 != "" {
|
||||
console.ErrStyle(console.Documentation, "Documentation: %s", p.URL)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -43,3 +44,23 @@ func MakeTempDir() string {
|
|||
os.Setenv(constants.MinikubeHome, tempDir)
|
||||
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