1
2
3
4
5 package main
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/importer"
11 "go/parser"
12 "go/token"
13 "go/types"
14 "log"
15 "path/filepath"
16 "strings"
17 "time"
18
19 "github.com/chai2010/gettext-go/po"
20 )
21
22 type Package struct {
23 path string
24 pkgpath string
25 pkgname string
26 filesAbspath []string
27
28 fset *token.FileSet
29 astFiles []*ast.File
30 typesInfo *types.Info
31 typesPackage *types.Package
32
33 potFile *po.File
34 }
35
36 func LoadPackage(path string) *Package {
37 p := &Package{
38 path: path,
39 pkgpath: gopkgPath(path),
40 pkgname: gopkgName(path),
41 filesAbspath: gopkgFilesAbspath(path),
42 }
43
44 var fset = token.NewFileSet()
45 var astFiles = make([]*ast.File, len(p.filesAbspath))
46
47 for i, path := range p.filesAbspath {
48 f, err := parser.ParseFile(fset, path, nil, 0)
49 if err != nil {
50 log.Fatal(err)
51 }
52 astFiles[i] = f
53 }
54
55
56 typesConfig := &types.Config{
57 Importer: importer.For("source", nil),
58 FakeImportC: true,
59 }
60 typesInfo := &types.Info{
61 Types: make(map[ast.Expr]types.TypeAndValue),
62 Defs: make(map[*ast.Ident]types.Object),
63 Uses: make(map[*ast.Ident]types.Object),
64 Selections: make(map[*ast.SelectorExpr]*types.Selection),
65 }
66
67 typesPackage, err := typesConfig.Check(p.pkgname, fset, astFiles, typesInfo)
68 if err != nil {
69 log.Fatal(err)
70 }
71
72 p.fset = fset
73 p.astFiles = astFiles
74 p.typesInfo = typesInfo
75 p.typesPackage = typesPackage
76
77 return p
78 }
79
80 func (p *Package) GenPotFile() *po.File {
81 p.potFile = &po.File{
82 MimeHeader: po.Header{
83 Comment: po.Comment{
84 TranslatorComment: "" +
85 fmt.Sprintf("package: %s\n\n", p.pkgpath) +
86 "Generated By gettext-go" + "\n" +
87 "https://github.com/chai2010/gettext-go" + "\n",
88 },
89 ProjectIdVersion: "1.0",
90 POTCreationDate: time.Now().Format("2006-01-02 15:04-0700"),
91 LanguageTeam: "golang-china",
92 Language: "zh_CN",
93 MimeVersion: "MIME-Version: 1.0",
94 ContentType: "Content-Type: text/plain; charset=UTF-8",
95 ContentTransferEncoding: "Content-Transfer-Encoding: 8bit",
96 },
97 }
98
99 for _, f := range p.astFiles {
100 ast.Inspect(f, func(n ast.Node) bool {
101 switch x := n.(type) {
102 case *ast.CallExpr:
103 switch sel := x.Fun.(type) {
104 case *ast.SelectorExpr:
105 if p.isGettextPackage(sel.X) {
106 p.processGettext(x, sel.Sel.Name)
107 }
108 }
109 }
110 return true
111 })
112 }
113
114 return p.potFile
115 }
116
117 func (p *Package) isGettextPackage(node ast.Node) bool {
118 inner := p.typesPackage.Scope().Innermost(node.Pos())
119 if ident, ok := node.(*ast.Ident); ok {
120 if _, obj := inner.LookupParent(ident.Name, node.Pos()); obj != nil {
121 if pkgName, ok := obj.(*types.PkgName); ok {
122 if pkg := pkgName.Imported(); pkg != nil {
123 if pkg.Path() == "github.com/chai2010/gettext-go" {
124 return true
125 }
126 }
127 }
128 }
129 }
130 return false
131 }
132
133 func (p *Package) processGettext(x *ast.CallExpr, fnName string) {
134 switch fnName {
135 case "Gettext":
136 pos := p.fset.Position(x.Pos())
137 p.potFile.Messages = append(p.potFile.Messages, po.Message{
138 Comment: po.Comment{
139 ReferenceFile: []string{p.pkgpath + "/" + filepath.Base(pos.Filename)},
140 ReferenceLine: []int{pos.Line},
141 Flags: []string{"go-format"},
142 },
143 MsgContext: "",
144 MsgId: p.evalStringValue(x.Args[0]),
145 })
146
147 case "PGettext":
148 pos := p.fset.Position(x.Pos())
149 p.potFile.Messages = append(p.potFile.Messages, po.Message{
150 Comment: po.Comment{
151 ReferenceFile: []string{p.pkgpath + "/" + filepath.Base(pos.Filename)},
152 ReferenceLine: []int{pos.Line},
153 Flags: []string{"go-format"},
154 },
155 MsgContext: p.evalStringValue(x.Args[0]),
156 MsgId: p.evalStringValue(x.Args[1]),
157 })
158
159 case "NGettext":
160
161 case "PNGettext":
162
163
164 case "DGettext":
165
166 case "DPGettext":
167
168 case "DNGettext":
169
170 case "DPNGettext":
171
172 }
173 }
174
175 func (p *Package) evalStringValue(val interface{}) string {
176 switch val.(type) {
177 case *ast.BasicLit:
178 s := val.(*ast.BasicLit).Value
179 s = strings.TrimSpace(s)
180 s = strings.Trim(s, `"`+"\n")
181 return s
182 case *ast.BinaryExpr:
183 if val.(*ast.BinaryExpr).Op != token.ADD {
184 return ""
185 }
186 left := p.evalStringValue(val.(*ast.BinaryExpr).X)
187 right := p.evalStringValue(val.(*ast.BinaryExpr).Y)
188 return left[0:len(left)-1] + right[1:len(right)]
189 default:
190 panic(fmt.Sprintf("unknown type: %v", val))
191 }
192 }
193
View as plain text