OSDN Git Service

fix commands
[bytom/shuttle.git] / vendor / github.com / bytom / vendor / golang.org / x / text / cmd / gotext / extract.go
diff --git a/vendor/github.com/bytom/vendor/golang.org/x/text/cmd/gotext/extract.go b/vendor/github.com/bytom/vendor/golang.org/x/text/cmd/gotext/extract.go
new file mode 100644 (file)
index 0000000..79a9b59
--- /dev/null
@@ -0,0 +1,195 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+       "go/ast"
+       "go/build"
+       "go/constant"
+       "go/format"
+       "go/parser"
+       "go/types"
+       "io/ioutil"
+       "os"
+       "path"
+       "path/filepath"
+       "strings"
+
+       "golang.org/x/tools/go/loader"
+)
+
+// TODO:
+// - merge information into existing files
+// - handle different file formats (PO, XLIFF)
+// - handle features (gender, plural)
+// - message rewriting
+
+var cmdExtract = &Command{
+       Run:       runExtract,
+       UsageLine: "extract <package>*",
+       Short:     "extract strings to be translated from code",
+}
+
+func runExtract(cmd *Command, args []string) error {
+       if len(args) == 0 {
+               args = []string{"."}
+       }
+
+       conf := loader.Config{
+               Build:      &build.Default,
+               ParserMode: parser.ParseComments,
+       }
+
+       // Use the initial packages from the command line.
+       args, err := conf.FromArgs(args, false)
+       if err != nil {
+               return err
+       }
+
+       // Load, parse and type-check the whole program.
+       iprog, err := conf.Load()
+       if err != nil {
+               return err
+       }
+
+       // print returns Go syntax for the specified node.
+       print := func(n ast.Node) string {
+               var buf bytes.Buffer
+               format.Node(&buf, conf.Fset, n)
+               return buf.String()
+       }
+
+       var translations []Translation
+
+       for _, info := range iprog.InitialPackages() {
+               for _, f := range info.Files {
+                       // Associate comments with nodes.
+                       cmap := ast.NewCommentMap(iprog.Fset, f, f.Comments)
+                       getComment := func(n ast.Node) string {
+                               cs := cmap.Filter(n).Comments()
+                               if len(cs) > 0 {
+                                       return strings.TrimSpace(cs[0].Text())
+                               }
+                               return ""
+                       }
+
+                       // Find function calls.
+                       ast.Inspect(f, func(n ast.Node) bool {
+                               call, ok := n.(*ast.CallExpr)
+                               if !ok {
+                                       return true
+                               }
+
+                               // Skip calls of functions other than
+                               // (*message.Printer).{Sp,Fp,P}rintf.
+                               sel, ok := call.Fun.(*ast.SelectorExpr)
+                               if !ok {
+                                       return true
+                               }
+                               meth := info.Selections[sel]
+                               if meth == nil || meth.Kind() != types.MethodVal {
+                                       return true
+                               }
+                               // TODO: remove cheap hack and check if the type either
+                               // implements some interface or is specifically of type
+                               // "golang.org/x/text/message".Printer.
+                               m, ok := extractFuncs[path.Base(meth.Recv().String())]
+                               if !ok {
+                                       return true
+                               }
+
+                               // argn is the index of the format string.
+                               argn, ok := m[meth.Obj().Name()]
+                               if !ok || argn >= len(call.Args) {
+                                       return true
+                               }
+
+                               // Skip calls with non-constant format string.
+                               fmtstr := info.Types[call.Args[argn]].Value
+                               if fmtstr == nil || fmtstr.Kind() != constant.String {
+                                       return true
+                               }
+
+                               posn := conf.Fset.Position(call.Lparen)
+                               filepos := fmt.Sprintf("%s:%d:%d", filepath.Base(posn.Filename), posn.Line, posn.Column)
+
+                               // TODO: identify the type of the format argument. If it is not
+                               // a string, multiple keys may be defined.
+                               var key []string
+
+                               // TODO: replace substitutions (%v) with a translator friendly
+                               // notation. For instance:
+                               //     "%d files remaining" -> "{numFiles} files remaining", or
+                               //     "%d files remaining" -> "{arg1} files remaining"
+                               // Alternatively, this could be done at a later stage.
+                               msg := constant.StringVal(fmtstr)
+
+                               // Construct a Translation unit.
+                               c := Translation{
+                                       Key:              key,
+                                       Position:         filepath.Join(info.Pkg.Path(), filepos),
+                                       Original:         Text{Msg: msg},
+                                       ExtractedComment: getComment(call.Args[0]),
+                                       // TODO(fix): this doesn't get the before comment.
+                                       // Comment: getComment(call),
+                               }
+
+                               for i, arg := range call.Args[argn+1:] {
+                                       var val string
+                                       if v := info.Types[arg].Value; v != nil {
+                                               val = v.ExactString()
+                                       }
+                                       posn := conf.Fset.Position(arg.Pos())
+                                       filepos := fmt.Sprintf("%s:%d:%d", filepath.Base(posn.Filename), posn.Line, posn.Column)
+                                       c.Args = append(c.Args, Argument{
+                                               ID:             i + 1,
+                                               Type:           info.Types[arg].Type.String(),
+                                               UnderlyingType: info.Types[arg].Type.Underlying().String(),
+                                               Expr:           print(arg),
+                                               Value:          val,
+                                               Comment:        getComment(arg),
+                                               Position:       filepath.Join(info.Pkg.Path(), filepos),
+                                               // TODO report whether it implements
+                                               // interfaces plural.Interface,
+                                               // gender.Interface.
+                                       })
+                               }
+
+                               translations = append(translations, c)
+                               return true
+                       })
+               }
+       }
+
+       data, err := json.MarshalIndent(translations, "", "    ")
+       if err != nil {
+               return err
+       }
+       for _, tag := range getLangs() {
+               // TODO: merge with existing files, don't overwrite.
+               os.MkdirAll(*dir, 0744)
+               file := filepath.Join(*dir, fmt.Sprintf("gotext_%v.out.json", tag))
+               if err := ioutil.WriteFile(file, data, 0744); err != nil {
+                       return fmt.Errorf("could not create file: %v", err)
+               }
+       }
+       return nil
+}
+
+// extractFuncs indicates the types and methods for which to extract strings,
+// and which argument to extract.
+// TODO: use the types in conf.Import("golang.org/x/text/message") to extract
+// the correct instances.
+var extractFuncs = map[string]map[string]int{
+       // TODO: Printer -> *golang.org/x/text/message.Printer
+       "message.Printer": {
+               "Printf":  0,
+               "Sprintf": 0,
+               "Fprintf": 1,
+       },
+}