Moves pkg/kubectl/util/i18n to staging
Kubernetes-commit: 70984d83858eef3c9c7d046f84d45c53aead673a
This commit is contained in:
parent
a3f4769375
commit
c1971f30ff
18
go.mod
18
go.mod
|
@ -7,6 +7,7 @@ go 1.12
|
|||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5
|
||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
|
||||
|
@ -19,10 +20,10 @@ require (
|
|||
github.com/spf13/pflag v1.0.3
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
k8s.io/api v0.0.0-20190726022912-69e1bce1dad5
|
||||
k8s.io/apimachinery v0.0.0-20190726022757-641a75999153
|
||||
k8s.io/cli-runtime v0.0.0-20190726024606-74a61cd71909
|
||||
k8s.io/client-go v0.0.0-20190726023111-a9c895e7f2ac
|
||||
k8s.io/api v0.0.0
|
||||
k8s.io/apimachinery v0.0.0
|
||||
k8s.io/cli-runtime v0.0.0
|
||||
k8s.io/client-go v0.0.0
|
||||
k8s.io/klog v0.3.1
|
||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a
|
||||
)
|
||||
|
@ -34,8 +35,9 @@ replace (
|
|||
golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503
|
||||
golang.org/x/text => golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db
|
||||
golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9
|
||||
k8s.io/api => k8s.io/api v0.0.0-20190726022912-69e1bce1dad5
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190726022757-641a75999153
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20190726024606-74a61cd71909
|
||||
k8s.io/client-go => k8s.io/client-go v0.0.0-20190725230141-579ad46bdcb9
|
||||
k8s.io/api => ../api
|
||||
k8s.io/apimachinery => ../apimachinery
|
||||
k8s.io/cli-runtime => ../cli-runtime
|
||||
k8s.io/client-go => ../client-go
|
||||
k8s.io/kubectl => ../kubectl
|
||||
)
|
||||
|
|
6
go.sum
6
go.sum
|
@ -13,6 +13,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko
|
|||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
|
@ -181,10 +183,6 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
k8s.io/api v0.0.0-20190726022912-69e1bce1dad5/go.mod h1:V6cpJ9D7WqSy0wqcE096gcbj+W//rshgQgmj1Shdwi8=
|
||||
k8s.io/apimachinery v0.0.0-20190726022757-641a75999153/go.mod h1:eXR4ljjmbwK6Ng0PKsXRySPXnTUy/qBUa6kPDeckhQ0=
|
||||
k8s.io/cli-runtime v0.0.0-20190726024606-74a61cd71909/go.mod h1:bk/fSEmINmKG2jHCCbqbXmwEJgE6kHVMkOC1U9dclzo=
|
||||
k8s.io/client-go v0.0.0-20190725230141-579ad46bdcb9/go.mod h1:ncT9fCvHnM5BUiZs0RCf9vAEqRrRoJtR2sZ2evompEU=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 i18n
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubectl/pkg/generated"
|
||||
|
||||
"github.com/chai2010/gettext-go/gettext"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
var knownTranslations = map[string][]string{
|
||||
"kubectl": {
|
||||
"default",
|
||||
"en_US",
|
||||
"fr_FR",
|
||||
"zh_CN",
|
||||
"ja_JP",
|
||||
"zh_TW",
|
||||
"it_IT",
|
||||
"de_DE",
|
||||
"ko_KR",
|
||||
},
|
||||
// only used for unit tests.
|
||||
"test": {
|
||||
"default",
|
||||
"en_US",
|
||||
},
|
||||
}
|
||||
|
||||
func loadSystemLanguage() string {
|
||||
// Implements the following locale priority order: LC_ALL, LC_MESSAGES, LANG
|
||||
// Similarly to: https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
|
||||
langStr := os.Getenv("LC_ALL")
|
||||
if langStr == "" {
|
||||
langStr = os.Getenv("LC_MESSAGES")
|
||||
}
|
||||
if langStr == "" {
|
||||
langStr = os.Getenv("LANG")
|
||||
}
|
||||
|
||||
if langStr == "" {
|
||||
klog.V(3).Infof("Couldn't find the LC_ALL, LC_MESSAGES or LANG environment variables, defaulting to en_US")
|
||||
return "default"
|
||||
}
|
||||
pieces := strings.Split(langStr, ".")
|
||||
if len(pieces) != 2 {
|
||||
klog.V(3).Infof("Unexpected system language (%s), defaulting to en_US", langStr)
|
||||
return "default"
|
||||
}
|
||||
return pieces[0]
|
||||
}
|
||||
|
||||
func findLanguage(root string, getLanguageFn func() string) string {
|
||||
langStr := getLanguageFn()
|
||||
|
||||
translations := knownTranslations[root]
|
||||
if translations != nil {
|
||||
for ix := range translations {
|
||||
if translations[ix] == langStr {
|
||||
return langStr
|
||||
}
|
||||
}
|
||||
}
|
||||
klog.V(3).Infof("Couldn't find translations for %s, using default", langStr)
|
||||
return "default"
|
||||
}
|
||||
|
||||
// LoadTranslations loads translation files. getLanguageFn should return a language
|
||||
// string (e.g. 'en-US'). If getLanguageFn is nil, then the loadSystemLanguage function
|
||||
// is used, which uses the 'LANG' environment variable.
|
||||
func LoadTranslations(root string, getLanguageFn func() string) error {
|
||||
if getLanguageFn == nil {
|
||||
getLanguageFn = loadSystemLanguage
|
||||
}
|
||||
|
||||
langStr := findLanguage(root, getLanguageFn)
|
||||
translationFiles := []string{
|
||||
fmt.Sprintf("%s/%s/LC_MESSAGES/k8s.po", root, langStr),
|
||||
fmt.Sprintf("%s/%s/LC_MESSAGES/k8s.mo", root, langStr),
|
||||
}
|
||||
|
||||
klog.V(3).Infof("Setting language to %s", langStr)
|
||||
// TODO: list the directory and load all files.
|
||||
buf := new(bytes.Buffer)
|
||||
w := zip.NewWriter(buf)
|
||||
|
||||
// Make sure to check the error on Close.
|
||||
for _, file := range translationFiles {
|
||||
filename := "translations/" + file
|
||||
f, err := w.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := generated.Asset(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.Write(data); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
gettext.BindTextdomain("k8s", root+".zip", buf.Bytes())
|
||||
gettext.Textdomain("k8s")
|
||||
gettext.SetLocale(langStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// T translates a string, possibly substituting arguments into it along
|
||||
// the way. If len(args) is > 0, args1 is assumed to be the plural value
|
||||
// and plural translation is used.
|
||||
func T(defaultValue string, args ...int) string {
|
||||
if len(args) == 0 {
|
||||
return gettext.PGettext("", defaultValue)
|
||||
}
|
||||
return fmt.Sprintf(gettext.PNGettext("", defaultValue, defaultValue+".plural", args[0]),
|
||||
args[0])
|
||||
}
|
||||
|
||||
// Errorf produces an error with a translated error string.
|
||||
// Substitution is performed via the `T` function above, following
|
||||
// the same rules.
|
||||
func Errorf(defaultValue string, args ...int) error {
|
||||
return errors.New(T(defaultValue, args...))
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 i18n
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var knownTestLocale = "en_US.UTF-8"
|
||||
|
||||
func TestTranslation(t *testing.T) {
|
||||
err := LoadTranslations("test", func() string { return "default" })
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
result := T("test_string")
|
||||
if result != "foo" {
|
||||
t.Errorf("expected: %s, saw: %s", "foo", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslationPlural(t *testing.T) {
|
||||
err := LoadTranslations("test", func() string { return "default" })
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
result := T("test_plural", 3)
|
||||
if result != "there were 3 items" {
|
||||
t.Errorf("expected: %s, saw: %s", "there were 3 items", result)
|
||||
}
|
||||
|
||||
result = T("test_plural", 1)
|
||||
if result != "there was 1 item" {
|
||||
t.Errorf("expected: %s, saw: %s", "there was 1 item", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslationUsingEnvVar(t *testing.T) {
|
||||
// We must backup and restore env vars before setting test values in tests
|
||||
// othervise we are risking to break other tests/test cases
|
||||
// which rely on the same env vars
|
||||
envVarsToBackup := []string{"LC_MESSAGES", "LANG", "LC_ALL"}
|
||||
expectedStrEnUSLocale := "baz"
|
||||
expectedStrFallback := "foo"
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
setenvFn func()
|
||||
expectedStr string
|
||||
}{
|
||||
{
|
||||
name: "Only LC_ALL is set",
|
||||
setenvFn: func() { os.Setenv("LC_ALL", knownTestLocale) },
|
||||
expectedStr: expectedStrEnUSLocale,
|
||||
},
|
||||
{
|
||||
name: "Only LC_MESSAGES is set",
|
||||
setenvFn: func() { os.Setenv("LC_MESSAGES", knownTestLocale) },
|
||||
expectedStr: expectedStrEnUSLocale,
|
||||
},
|
||||
{
|
||||
name: "Only LANG",
|
||||
setenvFn: func() { os.Setenv("LANG", knownTestLocale) },
|
||||
expectedStr: expectedStrEnUSLocale,
|
||||
},
|
||||
{
|
||||
name: "LC_MESSAGES overrides LANG",
|
||||
setenvFn: func() {
|
||||
os.Setenv("LANG", "be_BY.UTF-8") // Unknown locale
|
||||
os.Setenv("LC_MESSAGES", knownTestLocale)
|
||||
},
|
||||
expectedStr: expectedStrEnUSLocale,
|
||||
},
|
||||
{
|
||||
name: "LC_ALL overrides LANG",
|
||||
setenvFn: func() {
|
||||
os.Setenv("LANG", "be_BY.UTF-8") // Unknown locale
|
||||
os.Setenv("LC_ALL", knownTestLocale)
|
||||
},
|
||||
expectedStr: expectedStrEnUSLocale,
|
||||
},
|
||||
{
|
||||
name: "LC_ALL overrides LC_MESSAGES",
|
||||
setenvFn: func() {
|
||||
os.Setenv("LC_MESSAGES", "be_BY.UTF-8") // Unknown locale
|
||||
os.Setenv("LC_ALL", knownTestLocale)
|
||||
},
|
||||
expectedStr: expectedStrEnUSLocale,
|
||||
},
|
||||
{
|
||||
name: "Unknown locale in LANG",
|
||||
setenvFn: func() { os.Setenv("LANG", "be_BY.UTF-8") },
|
||||
expectedStr: expectedStrFallback,
|
||||
},
|
||||
{
|
||||
name: "Unknown locale in LC_MESSAGES",
|
||||
setenvFn: func() { os.Setenv("LC_MESSAGES", "be_BY.UTF-8") },
|
||||
expectedStr: expectedStrFallback,
|
||||
},
|
||||
{
|
||||
name: "Unknown locale in LC_ALL",
|
||||
setenvFn: func() { os.Setenv("LC_ALL", "be_BY.UTF-8") },
|
||||
expectedStr: expectedStrFallback,
|
||||
},
|
||||
{
|
||||
name: "Invalid env var",
|
||||
setenvFn: func() { os.Setenv("LC_MESSAGES", "fake.locale.UTF-8") },
|
||||
expectedStr: expectedStrFallback,
|
||||
},
|
||||
{
|
||||
name: "No env vars",
|
||||
setenvFn: func() {},
|
||||
expectedStr: expectedStrFallback,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, envVar := range envVarsToBackup {
|
||||
if envVarValue := os.Getenv(envVar); envVarValue != "" {
|
||||
os.Unsetenv(envVar)
|
||||
// Restore env var at the end
|
||||
defer func() { os.Setenv(envVar, envVarValue) }()
|
||||
}
|
||||
}
|
||||
|
||||
test.setenvFn()
|
||||
|
||||
err := LoadTranslations("test", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
result := T("test_string")
|
||||
if result != test.expectedStr {
|
||||
t.Errorf("expected: %s, saw: %s", test.expectedStr, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue