1
16
17 package i18n
18
19 import (
20 "archive/zip"
21 "bytes"
22 "embed"
23 "errors"
24 "fmt"
25 "os"
26 "strings"
27 "sync"
28
29 "github.com/chai2010/gettext-go"
30
31 "k8s.io/klog/v2"
32 )
33
34
35 var translations embed.FS
36
37 var knownTranslations = map[string][]string{
38 "kubectl": {
39 "default",
40 "en_US",
41 "fr_FR",
42 "zh_CN",
43 "ja_JP",
44 "zh_TW",
45 "it_IT",
46 "de_DE",
47 "ko_KR",
48 "pt_BR",
49 },
50
51 "test": {
52 "default",
53 "en_US",
54 },
55 }
56
57 var (
58 lazyLoadTranslationsOnce sync.Once
59 LoadTranslationsFunc = func() error {
60 return LoadTranslations("kubectl", nil)
61 }
62 translationsLoaded bool
63 )
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 func SetLoadTranslationsFunc(f func() error) error {
94 if translationsLoaded {
95 return errors.New("translations have already been loaded")
96 }
97 LoadTranslationsFunc = func() error {
98 if err := f(); err != nil {
99 return err
100 }
101 translationsLoaded = true
102 return nil
103 }
104 return nil
105 }
106
107 func loadSystemLanguage() string {
108
109
110 langStr := os.Getenv("LC_ALL")
111 if langStr == "" {
112 langStr = os.Getenv("LC_MESSAGES")
113 }
114 if langStr == "" {
115 langStr = os.Getenv("LANG")
116 }
117
118 if langStr == "" {
119 klog.V(3).Infof("Couldn't find the LC_ALL, LC_MESSAGES or LANG environment variables, defaulting to en_US")
120 return "default"
121 }
122 pieces := strings.Split(langStr, ".")
123 if len(pieces) != 2 {
124 klog.V(3).Infof("Unexpected system language (%s), defaulting to en_US", langStr)
125 return "default"
126 }
127 return pieces[0]
128 }
129
130 func findLanguage(root string, getLanguageFn func() string) string {
131 langStr := getLanguageFn()
132
133 translations := knownTranslations[root]
134 for ix := range translations {
135 if translations[ix] == langStr {
136 return langStr
137 }
138 }
139 klog.V(3).Infof("Couldn't find translations for %s, using default", langStr)
140 return "default"
141 }
142
143
144
145
146 func LoadTranslations(root string, getLanguageFn func() string) error {
147 if getLanguageFn == nil {
148 getLanguageFn = loadSystemLanguage
149 }
150
151 langStr := findLanguage(root, getLanguageFn)
152 translationFiles := []string{
153 fmt.Sprintf("%s/%s/LC_MESSAGES/k8s.po", root, langStr),
154 fmt.Sprintf("%s/%s/LC_MESSAGES/k8s.mo", root, langStr),
155 }
156
157 klog.V(3).Infof("Setting language to %s", langStr)
158
159 buf := new(bytes.Buffer)
160 w := zip.NewWriter(buf)
161
162
163 for _, file := range translationFiles {
164 filename := "translations/" + file
165 f, err := w.Create(file)
166 if err != nil {
167 return err
168 }
169 data, err := translations.ReadFile(filename)
170 if err != nil {
171 return err
172 }
173 if _, err := f.Write(data); err != nil {
174 return nil
175 }
176 }
177 if err := w.Close(); err != nil {
178 return err
179 }
180 gettext.BindLocale(gettext.New("k8s", root+".zip", buf.Bytes()))
181 gettext.SetDomain("k8s")
182 gettext.SetLanguage(langStr)
183 translationsLoaded = true
184 return nil
185 }
186
187 func lazyLoadTranslations() {
188 lazyLoadTranslationsOnce.Do(func() {
189 if translationsLoaded {
190 return
191 }
192 if err := LoadTranslationsFunc(); err != nil {
193 klog.Warning("Failed to load translations")
194 }
195 })
196 }
197
198
199
200
201 func T(defaultValue string, args ...int) string {
202 lazyLoadTranslations()
203 if len(args) == 0 {
204 return gettext.PGettext("", defaultValue)
205 }
206 return fmt.Sprintf(gettext.PNGettext("", defaultValue, defaultValue+".plural", args[0]),
207 args[0])
208 }
209
210
211
212
213 func Errorf(defaultValue string, args ...int) error {
214 return errors.New(T(defaultValue, args...))
215 }
216
View as plain text