podman
274 строки · 5.6 Кб
1package ut
2
3import (
4"encoding/json"
5"fmt"
6"os"
7"path/filepath"
8
9"io"
10
11"github.com/go-playground/locales"
12)
13
14type translation struct {
15Locale string `json:"locale"`
16Key interface{} `json:"key"` // either string or integer
17Translation string `json:"trans"`
18PluralType string `json:"type,omitempty"`
19PluralRule string `json:"rule,omitempty"`
20OverrideExisting bool `json:"override,omitempty"`
21}
22
23const (
24cardinalType = "Cardinal"
25ordinalType = "Ordinal"
26rangeType = "Range"
27)
28
29// ImportExportFormat is the format of the file import or export
30type ImportExportFormat uint8
31
32// supported Export Formats
33const (
34FormatJSON ImportExportFormat = iota
35)
36
37// Export writes the translations out to a file on disk.
38//
39// NOTE: this currently only works with string or int translations keys.
40func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error {
41
42_, err := os.Stat(dirname)
43if err != nil {
44
45if !os.IsNotExist(err) {
46return err
47}
48
49if err = os.MkdirAll(dirname, 0744); err != nil {
50return err
51}
52}
53
54// build up translations
55var trans []translation
56var b []byte
57var ext string
58
59for _, locale := range t.translators {
60
61for k, v := range locale.(*translator).translations {
62trans = append(trans, translation{
63Locale: locale.Locale(),
64Key: k,
65Translation: v.text,
66})
67}
68
69for k, pluralTrans := range locale.(*translator).cardinalTanslations {
70
71for i, plural := range pluralTrans {
72
73// leave enough for all plural rules
74// but not all are set for all languages.
75if plural == nil {
76continue
77}
78
79trans = append(trans, translation{
80Locale: locale.Locale(),
81Key: k.(string),
82Translation: plural.text,
83PluralType: cardinalType,
84PluralRule: locales.PluralRule(i).String(),
85})
86}
87}
88
89for k, pluralTrans := range locale.(*translator).ordinalTanslations {
90
91for i, plural := range pluralTrans {
92
93// leave enough for all plural rules
94// but not all are set for all languages.
95if plural == nil {
96continue
97}
98
99trans = append(trans, translation{
100Locale: locale.Locale(),
101Key: k.(string),
102Translation: plural.text,
103PluralType: ordinalType,
104PluralRule: locales.PluralRule(i).String(),
105})
106}
107}
108
109for k, pluralTrans := range locale.(*translator).rangeTanslations {
110
111for i, plural := range pluralTrans {
112
113// leave enough for all plural rules
114// but not all are set for all languages.
115if plural == nil {
116continue
117}
118
119trans = append(trans, translation{
120Locale: locale.Locale(),
121Key: k.(string),
122Translation: plural.text,
123PluralType: rangeType,
124PluralRule: locales.PluralRule(i).String(),
125})
126}
127}
128
129switch format {
130case FormatJSON:
131b, err = json.MarshalIndent(trans, "", " ")
132ext = ".json"
133}
134
135if err != nil {
136return err
137}
138
139err = os.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644)
140if err != nil {
141return err
142}
143
144trans = trans[0:0]
145}
146
147return nil
148}
149
150// Import reads the translations out of a file or directory on disk.
151//
152// NOTE: this currently only works with string or int translations keys.
153func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error {
154
155fi, err := os.Stat(dirnameOrFilename)
156if err != nil {
157return err
158}
159
160processFn := func(filename string) error {
161
162f, err := os.Open(filename)
163if err != nil {
164return err
165}
166defer f.Close()
167
168return t.ImportByReader(format, f)
169}
170
171if !fi.IsDir() {
172return processFn(dirnameOrFilename)
173}
174
175// recursively go through directory
176walker := func(path string, info os.FileInfo, err error) error {
177
178if info.IsDir() {
179return nil
180}
181
182switch format {
183case FormatJSON:
184// skip non JSON files
185if filepath.Ext(info.Name()) != ".json" {
186return nil
187}
188}
189
190return processFn(path)
191}
192
193return filepath.Walk(dirnameOrFilename, walker)
194}
195
196// ImportByReader imports the the translations found within the contents read from the supplied reader.
197//
198// NOTE: generally used when assets have been embedded into the binary and are already in memory.
199func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error {
200
201b, err := io.ReadAll(reader)
202if err != nil {
203return err
204}
205
206var trans []translation
207
208switch format {
209case FormatJSON:
210err = json.Unmarshal(b, &trans)
211}
212
213if err != nil {
214return err
215}
216
217for _, tl := range trans {
218
219locale, found := t.FindTranslator(tl.Locale)
220if !found {
221return &ErrMissingLocale{locale: tl.Locale}
222}
223
224pr := stringToPR(tl.PluralRule)
225
226if pr == locales.PluralRuleUnknown {
227
228err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting)
229if err != nil {
230return err
231}
232
233continue
234}
235
236switch tl.PluralType {
237case cardinalType:
238err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
239case ordinalType:
240err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
241case rangeType:
242err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting)
243default:
244return &ErrBadPluralDefinition{tl: tl}
245}
246
247if err != nil {
248return err
249}
250}
251
252return nil
253}
254
255func stringToPR(s string) locales.PluralRule {
256
257switch s {
258case "Zero":
259return locales.PluralRuleZero
260case "One":
261return locales.PluralRuleOne
262case "Two":
263return locales.PluralRuleTwo
264case "Few":
265return locales.PluralRuleFew
266case "Many":
267return locales.PluralRuleMany
268case "Other":
269return locales.PluralRuleOther
270default:
271return locales.PluralRuleUnknown
272}
273
274}
275