/
i18ngenerator.go
164 lines (145 loc) · 4.61 KB
/
i18ngenerator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
i18ngenerator 是国际化的代码生成器,通过项目根目录下 i18n 文件夹中 message_xx.properties 文件生成可供 sdk 国际化模块使用的 go 代码
通过 go install github.com/ci-plugins/golang-plugin-sdk/cmd/i18ngenerator@latest 命令安装
在使用国际化中的文件输入 //go:generate i18ngenerator [i8n 文件地址] [生成的 go 文件路径]
同时在项目根目录下 go generate . 即可生成具体的国际化代码
同时使用 sdk 中的 InitI18n 即可完成国际化功能初始化
*/
package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"github.com/magiconair/properties"
"golang.org/x/text/language"
)
const (
i18NFILE_PREFIX = "message_"
i18NFILE_SUFFIX = ".properties"
)
func main() {
var err error
fmt.Fprintf(os.Stdout, "start running translation_generator...\n")
if len(os.Args) < 3 {
fmt.Fprintf(os.Stderr, "args not enough, need i18n file path and output path\n")
exit()
}
i18nFileDir := os.Args[1]
if !filepath.IsAbs(i18nFileDir) {
if i18nFileDir, err = filepath.Abs(i18nFileDir); err != nil {
fmt.Fprintf(os.Stderr, "i18n file path abs error\n")
exit()
}
}
fmt.Fprintf(os.Stdout, "i18n file path dir %s\n", i18nFileDir)
outputName := os.Args[2]
if !filepath.IsAbs(i18nFileDir) {
if outputName, err = filepath.Abs(os.Args[2]); err != nil {
fmt.Fprintf(os.Stderr, "output file path abs error\n")
exit()
}
}
if !strings.HasSuffix(filepath.Base(outputName), ".go") {
fmt.Fprintf(os.Stderr, "output file must go file\n")
exit()
}
fmt.Fprintf(os.Stdout, "output file path %s\n", outputName)
g := Generator{}
// 打印文件头和包引用
g.Printf("// Code generated by \"i18ngenerator\"; DO NOT EDIT.\n")
g.Printf("\n")
g.Printf("package %s", strings.TrimSuffix(filepath.Base(outputName), ".go"))
g.Printf("\n")
g.Printf("// Translations\n")
g.Printf("var Translations map[string][][]string = make(map[string][][]string)\n")
g.Printf("func init(){\n")
// 读取用户配置的国际化文件
files, err := ioutil.ReadDir(i18nFileDir)
if err != nil {
fmt.Fprintf(os.Stderr, "read i18ndir error %s \n", err.Error())
exit()
}
for _, f := range files {
if f.IsDir() {
continue
}
// 文件名称就作为需要国际化的语言的key
fileName := f.Name()
// 不符合类似 massage_zh_CN.properties 的都不参与生成
if !strings.HasPrefix(fileName, i18NFILE_PREFIX) || !strings.HasSuffix(fileName, i18NFILE_SUFFIX) {
continue
}
fmt.Fprintf(os.Stdout, "start load language file %s ...\n", fileName)
// 解析国际化内容
properties, err := properties.LoadFile(filepath.Join(i18nFileDir, fileName), properties.UTF8)
if err != nil {
fmt.Fprintf(os.Stderr, "load i18nfile %s error %s \n", filepath.Join(i18nFileDir, fileName), err.Error())
exit()
}
// 拿到id排序方便每次输出一致
keys := []string{}
keys = append(keys, properties.Keys()...)
sort.Strings(keys)
// 打印代码内容
// 获取并校验语言类型
lanuagStr := strings.TrimSuffix(strings.TrimPrefix(fileName, i18NFILE_PREFIX), i18NFILE_SUFFIX)
lanuagTag, err := language.Parse(lanuagStr)
if err != nil {
fmt.Fprintf(os.Stderr, "go not support lanuage name %s error %s\n", lanuagStr, err.Error())
exit()
}
g.Printf("Translations[\"%s\"] = [][]string{\n", lanuagTag.String())
// 拼接 i18n.Message 对象,并校验
for _, id := range keys {
v := properties.GetString(id, "")
g.Printf("{\n")
if id == "" {
fmt.Fprintf(os.Stderr, "build i18nmessage error file %s id %s is blank \n", filepath.Join(i18nFileDir, fileName), id)
exit()
}
g.Printf("\"%s\",\n", id)
// 将换行符转义
value := strings.ReplaceAll(v, "\n", "\\n")
g.Printf("\"%s\",\n", value)
g.Printf("},\n")
}
g.Printf("}\n")
fmt.Fprintf(os.Stdout, "language file %s trans done\n", fileName)
}
g.Printf("}\n")
// 格式化输出
src := g.format()
err = os.MkdirAll(filepath.Dir(outputName), os.ModePerm)
if err != nil {
fmt.Fprintf(os.Stderr, "create output dir: %s", err)
exit()
}
err = os.WriteFile(outputName, src, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "writing output: %s", err)
exit()
}
}
// 生成器保存分析的状态。 主要用来缓冲 format.Source 的输出。
type Generator struct {
buf bytes.Buffer // 累计输出
}
func (g *Generator) Printf(format string, args ...interface{}) {
fmt.Fprintf(&g.buf, format, args...)
}
// 格式返回生成器缓冲区的 gofmt-ed 内容。
func (g *Generator) format() []byte {
src, err := format.Source(g.buf.Bytes())
if err != nil {
return g.buf.Bytes()
}
return src
}
func exit() {
os.Exit(1)
}