Moves pkg/kubectl/util/i18n to staging

Kubernetes-commit: 70984d83858eef3c9c7d046f84d45c53aead673a
This commit is contained in:
Sean Sullivan 2019-07-25 14:44:06 -07:00 committed by Kubernetes Publisher
parent a3f4769375
commit c1971f30ff
5 changed files with 23168 additions and 12 deletions

18
go.mod
View File

@ -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
View File

@ -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=

22850
pkg/generated/bindata.go Normal file

File diff suppressed because one or more lines are too long

149
pkg/util/i18n/i18n.go Normal file
View File

@ -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...))
}

157
pkg/util/i18n/i18n_test.go Normal file
View File

@ -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)
}
})
}
}