From 4ee4552d171620060fc489a1ae3d9af57046a6b0 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 22 May 2013 12:34:34 -0400 Subject: [PATCH] strings add i18n support --- README.md | 2 - build.go | 8 +- conf/gpm.toml | 2 +- doc/error.go | 1 - gpm.go | 68 ++++++++++++---- i18n/en-US/prompt.txt | 28 +++++++ install.go | 48 ++++++----- models/models.go | 185 ------------------------------------------ 8 files changed, 112 insertions(+), 230 deletions(-) create mode 100644 i18n/en-US/prompt.txt delete mode 100644 models/models.go diff --git a/README.md b/README.md index c7e0e4866..2f7596064 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ gpm(Go Package Manager) is a Go package manage tool for search, install, update ## Todo -- All errors should have specific title for exactly where were created. -- Add i18n support for all strings. - Command `build` add current path to GOPATH temporary. - Add gpm working principle design. - Add support for downloading tarballs from user sources. diff --git a/build.go b/build.go index 61cd986d5..cde20a05d 100644 --- a/build.go +++ b/build.go @@ -44,17 +44,17 @@ func runBuild(cmd *Command, args []string) { if utils.IsExist(wd + "/" + proName) { err := os.Remove(wd + "/" + proName) if err != nil { - fmt.Printf("Fail to remove file in current directory: %s.\n", err) + fmt.Printf(fmt.Sprintf("ERROR: %s\n", promptMsg["RemoveFile"]), err) return } } err := os.Rename(v+"/bin/"+proName, wd+"/"+proName) if err == nil { - fmt.Printf("Moved file from $GOPATH(%s) to current directory(%s).\n", v, wd) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["MovedFile"]), v, wd) return - } else { - fmt.Printf("Fail to move file from $GOPATH(%s) to current directory: %s.\n", v, err) } + + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["MoveFile"]), v, wd) break } } diff --git a/conf/gpm.toml b/conf/gpm.toml index 4d26e9454..0fc4dcfa5 100644 --- a/conf/gpm.toml +++ b/conf/gpm.toml @@ -1,7 +1,7 @@ # This is a configuration file for gpm with toml format. title = "gpm(Go Package Manager)" -version = "v0.1.2 Build 0522" +version = "v0.1.3 Build 0522" username = "" password = "" user_language = "en-US" \ No newline at end of file diff --git a/doc/error.go b/doc/error.go index 379d30a45..dc12c93f1 100644 --- a/doc/error.go +++ b/doc/error.go @@ -10,7 +10,6 @@ import ( var ( errNotModified = errors.New("package not modified") - ErrNoMatch = errors.New("no match") errUpdateTimeout = errors.New("update timeout") ) diff --git a/gpm.go b/gpm.go index 530f3a574..7d1ba03be 100644 --- a/gpm.go +++ b/gpm.go @@ -26,12 +26,18 @@ import ( ) var ( - config tomlConfig - appPath string // Application path. + config tomlConfig + appPath string // Application path. +) + +var ( localNodes []*doc.Node localBundles []*doc.Bundle ) +// Use for i18n, key is prompt code, value is corresponding message. +var promptMsg map[string]string + type tomlConfig struct { Title, Version string Username, Password string @@ -93,7 +99,7 @@ func getAppPath() bool { // Look up executable in PATH variable. appPath, _ = exec.LookPath("gpm") if len(appPath) == 0 { - fmt.Printf("getAppPath(): Unable to indicate current execute path.") + fmt.Printf("ERROR: getAppPath -> Unable to indicate current execute path.\n") return false } @@ -104,17 +110,47 @@ func getAppPath() bool { return true } +// loadPromptMsg loads prompt messages according to user language. +func loadPromptMsg(lang string) bool { + promptMsg = make(map[string]string) + + // Load prompt messages. + f, err := os.Open(appPath + "i18n/" + lang + "/prompt.txt") + if err != nil { + fmt.Printf("ERROR: loadUsage -> Fail to load prompt messages[ %s ]\n", err) + return false + } + defer f.Close() + + // Read prompt messages. + fi, _ := f.Stat() + promptBytes := make([]byte, fi.Size()) + f.Read(promptBytes) + promptStrs := strings.Split(string(promptBytes), "\n") + for _, p := range promptStrs { + i := strings.Index(p, "=") + if i > -1 { + promptMsg[p[:i]] = p[i+1:] + } + } + return true +} + // loadUsage loads usage according to user language. func loadUsage(lang string) bool { + if !loadPromptMsg(lang) { + return false + } + // Load main usage. f, err := os.Open(appPath + "i18n/" + lang + "/usage.tpl") if err != nil { - fmt.Printf("loadUsage(): Fail to load main usage: %s.\n", err) + fmt.Printf(fmt.Sprintf("ERROR: loadUsage -> %s\n", promptMsg["LoadCommandUsage"]), "main", err) return false } defer f.Close() - // Read command usages. + // Read main usages. fi, _ := f.Stat() usageBytes := make([]byte, fi.Size()) f.Read(usageBytes) @@ -124,17 +160,19 @@ func loadUsage(lang string) bool { for _, cmd := range commands { f, err := os.Open(appPath + "i18n/" + lang + "/usage_" + cmd.Name() + ".txt") if err != nil { - fmt.Printf("loadUsage(): Fail to load usage(%s): %s.\n", cmd.Name(), err) + fmt.Printf(fmt.Sprintf("ERROR: loadUsage -> %s\n", promptMsg["LoadCommandUsage"]), cmd.Name(), err) return false } defer f.Close() + // Read usage. fi, _ := f.Stat() usageBytes := make([]byte, fi.Size()) f.Read(usageBytes) usages := strings.Split(string(usageBytes), "|||") if len(usages) < 2 { - fmt.Printf("loadUsage(): nacceptable usage file: %s.\n", cmd.Name()) + fmt.Printf( + fmt.Sprintf("ERROR: loadUsage -> %s\n", promptMsg["ReadCoammndUsage"]), cmd.Name()) return false } cmd.Short = usages[0] @@ -151,14 +189,14 @@ func loadLocalNodes() bool { } else { fr, err := os.Open(appPath + "data/nodes.json") if err != nil { - fmt.Println(err) + fmt.Printf(fmt.Sprintf("ERROR: loadLocalNodes -> %s\n", promptMsg["LoadLocalData"]), err) return false } defer fr.Close() err = json.NewDecoder(fr).Decode(&localNodes) if err != nil && err != io.EOF { - fmt.Println(err) + fmt.Printf(fmt.Sprintf("ERROR: loadLocalNodes -> %s\n", promptMsg["ParseJSON"]), err) return false } } @@ -170,14 +208,14 @@ func loadLocalBundles() bool { // Find all bundles. dir, err := os.Open(appPath + "repo/bundles/") if err != nil { - fmt.Println(err) + fmt.Printf(fmt.Sprintf("ERROR: loadLocalBundles -> %s\n", promptMsg["OpenFile"]), err) return false } defer dir.Close() fis, err := dir.Readdir(0) if err != nil { - fmt.Println(err) + fmt.Printf(fmt.Sprintf("ERROR: loadLocalBundles -> %s\n", promptMsg["OpenFile"]), err) return false } @@ -186,7 +224,7 @@ func loadLocalBundles() bool { if !fi.IsDir() && strings.HasSuffix(fi.Name(), ".json") { fr, err := os.Open(appPath + "repo/bundles/" + fi.Name()) if err != nil { - fmt.Println(err) + fmt.Printf(fmt.Sprintf("ERROR: loadLocalBundles -> %s\n", promptMsg["OpenFile"]), err) return false } @@ -194,7 +232,7 @@ func loadLocalBundles() bool { err = json.NewDecoder(fr).Decode(bundle) fr.Close() if err != nil && err != io.EOF { - fmt.Println(err) + fmt.Printf(fmt.Sprintf("ERROR: loadLocalBundles -> %s\n", promptMsg["ParseJSON"]), err) return false } @@ -219,7 +257,7 @@ func initialize() bool { // Load configuration. if _, err := toml.DecodeFile(appPath+"conf/gpm.toml", &config); err != nil { - fmt.Println(err) + fmt.Printf("initialize -> Fail to load configuration[ %s ]\n", err) return false } @@ -269,7 +307,7 @@ func main() { } // Uknown commands. - fmt.Fprintf(os.Stderr, "gpm: unknown subcommand %q\nRun 'gpm help' for usage.\n", args[0]) + fmt.Fprintf(os.Stderr, fmt.Sprintf("%s\n", promptMsg["UnknownCommand"]), args[0]) setExitStatus(2) exit() } diff --git a/i18n/en-US/prompt.txt b/i18n/en-US/prompt.txt new file mode 100644 index 000000000..bc9d00b55 --- /dev/null +++ b/i18n/en-US/prompt.txt @@ -0,0 +1,28 @@ +LoadCommandUsage=Fail to load command(%s) usage[ %s ] +ReadCoammndUsage=Unacceptable command(%s) usage file. +LoadLocalData=Fail to load local data[ %s ] +ParseJSON=Fail to parse JSON[ %s ] +OpenFile=Fail to open file[ %s ] +RemoveFile=Fail to remove file[ %s ] +UnknownCommand=gpm: Unknown command %q. Run 'gpm help' for usage. +MoveFile=Fail to move file from $GOPATH(%s) to current directory(%s). +UnknownFlag=Unknown flag: %s. +DownloadError=Fail to download package(%s)[ %s ] +NotFoundError=Import path prefix matches known service, but regexp does not. +ErrNoMatch=Unsupported VCS platform. + +MovedFile=Moved file from $GOPATH(%s) to current directory(%s). +PureDownload=You enabled pure download. +DownloadOnly=You enabled download without installing. +DownloadExDeps=You enabled download dependencies in example. +DownloadFromSrcs=You enabled download from sources. +NoPackage=Please list at least one package/bundle/snapshot. +DownloadPath=Packages will be downloaded to GOPATH(%s). +InstallStatus=Installing package: %s. +BundleInfo=Bundle(%s) contains following nodes: +ContinueDownload=Continue to download?(Y/n). +SkipDownloaded=Skipped downloaded package: %s. +SkipInvalidPath=Skipped invalid import path: %s. +InstallByGoGet=Installing package(%s) through 'go get'. +NoVCSTool=No version control tool is available, pure download enabled! +DownloadStatus=Downloading package: %s. \ No newline at end of file diff --git a/install.go b/install.go index e08fc3730..32335cee0 100644 --- a/install.go +++ b/install.go @@ -6,6 +6,7 @@ package main import ( "encoding/json" + "errors" "fmt" "net/http" "os" @@ -44,13 +45,13 @@ func init() { func printPrompt(flag string) { switch flag { case "-p": - fmt.Printf("You enabled pure download.\n") + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["PureDownload"])) case "-d": - fmt.Printf("You enabled download without installing.\n") + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["DownloadOnly"])) case "-e": - fmt.Printf("You enabled download dependencies in example.\n") + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["DownloadExDeps"])) case "-s": - fmt.Printf("You enabled download from sources.\n") + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["DownloadFromSrcs"])) } } @@ -71,7 +72,7 @@ func checkFlags(args []string) int { cmdInstall.Flags[f] = true printPrompt(f) } else { - fmt.Printf("Unknown flag: %s.\n", f) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["UnknownFlag"]), f) return -1 } num = i + 1 @@ -103,7 +104,7 @@ func runInstall(cmd *Command, args []string) { // Check length of arguments. if len(args) < 1 { - fmt.Printf("Please list at least one package/bundle/snapshot.\n") + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["NoPackage"])) return } @@ -111,7 +112,7 @@ func runInstall(cmd *Command, args []string) { checkVCSTool() installGOPATH = utils.GetBestMatchGOPATH(appPath) - fmt.Printf("Packages will be downloaded to GOPATH(%s).\n", installGOPATH) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["DownloadPath"]), installGOPATH) // Generate temporary nodes. nodes := make([]*doc.Node, len(args)) @@ -129,7 +130,7 @@ func runInstall(cmd *Command, args []string) { cmdArgs = append(cmdArgs, "") for k := range downloadCache { - fmt.Printf("Installing package: %s.\n", k) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["InstallStatus"]), k) cmdArgs[1] = k executeGoCommand(cmdArgs) } @@ -137,11 +138,15 @@ func runInstall(cmd *Command, args []string) { // Save local nodes to file. fw, err := os.Create(appPath + "data/nodes.json") if err != nil { - fmt.Println(err) + fmt.Printf(fmt.Sprintf("ERROR: runInstall -> %s\n", promptMsg["OpenFile"]), err) return } defer fw.Close() - fbytes, _ := json.MarshalIndent(&localNodes, "", "\t") + fbytes, err := json.MarshalIndent(&localNodes, "", "\t") + if err != nil { + fmt.Printf(fmt.Sprintf("ERROR: runInstall -> %s\n", promptMsg["ParseJSON"]), err) + return + } fw.Write(fbytes) } @@ -183,14 +188,13 @@ func downloadPackages(nodes []*doc.Node) { case n.ImportPath[0] == 'B': // Check local bundles. bnodes := checkLocalBundles(n.ImportPath[1:]) - if len(nodes) > 0 { + if len(bnodes) > 0 { // Check with users if continue. - fmt.Printf("Bundle(%s) contains following nodes:\n", - n.ImportPath[1:]) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["BundleInfo"]), n.ImportPath[1:]) for _, bn := range bnodes { fmt.Printf("[%s] -> %s: %s.\n", bn.ImportPath, bn.Type, bn.Value) } - fmt.Print("Continue to download?(Y/n).") + fmt.Printf(fmt.Sprintf("%s", promptMsg["ContinueDownload"])) var option string fmt.Fscan(os.Stdin, &option) if strings.ToLower(option) != "y" { @@ -225,11 +229,11 @@ func downloadPackages(nodes []*doc.Node) { saveNode(node) } } else { - fmt.Printf("Skipped downloaded package: %s.\n", n.ImportPath) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["SkipDownloaded"]), n.ImportPath) } default: // Invalid import path. - fmt.Printf("Skipped invalid import path: %s.\n", n.ImportPath) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["SkipInvalidPath"]), n.ImportPath) } } } @@ -254,7 +258,7 @@ func downloadPackage(node *doc.Node) (*doc.Node, []string) { switch { case !cmdInstall.Flags["-p"] && ((node.ImportPath[0] == 'g' && isHasGit) || (node.ImportPath[0] == 'c' && isHasHg)): // github.com, code.google.com - fmt.Printf("Installing package(%s) through 'go get'.\n", node.ImportPath) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["InstallByGoGet"]), node.ImportPath) args := checkGoGetFlags() args = append(args, node.ImportPath) executeGoCommand(args) @@ -262,16 +266,16 @@ func downloadPackage(node *doc.Node) (*doc.Node, []string) { default: // Pure download. if !cmdInstall.Flags["-p"] { cmdInstall.Flags["-p"] = true - fmt.Printf("No version control tool is available, pure download enabled!\n") + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["NoVCSTool"])) } - fmt.Printf("Downloading package: %s.\n", node.ImportPath) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["InstallByGoGet"]), node.ImportPath) // Mark as donwloaded. downloadCache[node.ImportPath] = true imports, err := pureDownload(node) if err != nil { - fmt.Printf("Fail to download package(%s) with error: %s.\n", node.ImportPath, err) + fmt.Printf(fmt.Sprintf("%s\n", promptMsg["DownloadError"]), node.ImportPath, err) return nil, nil } @@ -317,7 +321,7 @@ func pureDownload(node *doc.Node) ([]string, error) { if m == nil { if s.prefix != "" { return nil, - doc.NotFoundError{"Import path prefix matches known service, but regexp does not."} + doc.NotFoundError{fmt.Sprintf("%s\n", promptMsg["NotFoundError"])} } continue } @@ -329,5 +333,5 @@ func pureDownload(node *doc.Node) ([]string, error) { } return s.get(doc.HttpClient, match, installGOPATH, node, cmdInstall.Flags) } - return nil, doc.ErrNoMatch + return nil, errors.New(fmt.Sprintf("%s\n", promptMsg["NotFoundError"])) } diff --git a/models/models.go b/models/models.go deleted file mode 100644 index 579351a8d..000000000 --- a/models/models.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) 2013 GPMGo Members. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -// Package models implemented database access funtions. - -package models - -import ( - "database/sql" - "errors" - //"os" - "strconv" - "strings" - "time" - - "github.com/coocood/qbs" - _ "github.com/mattn/go-sqlite3" -) - -const ( - DB_NAME = "./data/gowalker.db" - _SQLITE3_DRIVER = "sqlite3" -) - -// PkgInfo is package information. -type PkgInfo struct { - Id int64 - Path string `qbs:"index"` // Import path of package. - AbsPath string - Imports []string - Note string - Created time.Time `qbs:"index"` // Time when information last updated. - Commit string // Revision tag and project tags. -} - -func connDb() *qbs.Qbs { - // 'sql.Open' only returns error when unknown driver, so it's not necessary to check in other places. - db, err := sql.Open(_SQLITE3_DRIVER, DB_NAME) - if err != nil { - //beego.Error("models.connDb():", err) - } - q := qbs.New(db, qbs.NewSqlite3()) - return q -} - -func setMg() (*qbs.Migration, error) { - db, err := sql.Open(_SQLITE3_DRIVER, DB_NAME) - mg := qbs.NewMigration(db, DB_NAME, qbs.NewSqlite3()) - return mg, err -} - -/*func init() { - // Initialize database. - os.Mkdir("./data", os.ModePerm) - - // Connect to database. - q := connDb() - defer q.Db.Close() - - mg, err := setMg() - if err != nil { - beego.Error("models.init():", err) - } - defer mg.Db.Close() - - // Create data tables. - mg.CreateTableIfNotExists(new(PkgInfo)) - - beego.Trace("Initialized database ->", DB_NAME) -}*/ - -// GetProInfo returns package information from database. -func GetPkgInfo(path string) (*PkgInfo, error) { - // Check path length to reduce connect times. - if len(path) == 0 { - return nil, errors.New("models.GetPkgInfo(): Empty path as not found.") - } - - // Connect to database. - q := connDb() - defer q.Db.Close() - - pinfo := new(PkgInfo) - err := q.WhereEqual("path", path).Find(pinfo) - - return pinfo, err -} - -// GetGroupPkgInfo returns group of package infomration in order to reduce database connect times. -func GetGroupPkgInfo(paths []string) ([]*PkgInfo, error) { - // Connect to database. - q := connDb() - defer q.Db.Close() - - pinfos := make([]*PkgInfo, 0, len(paths)) - for _, v := range paths { - if len(v) > 0 { - pinfo := new(PkgInfo) - err := q.WhereEqual("path", v).Find(pinfo) - if err == nil { - pinfos = append(pinfos, pinfo) - } else { - pinfos = append(pinfos, &PkgInfo{Path: v}) - } - } - } - return pinfos, nil -} - -// GetPkgInfoById returns package information from database by pid. -func GetPkgInfoById(pid int) (*PkgInfo, error) { - // Connect to database. - q := connDb() - defer q.Db.Close() - - pinfo := new(PkgInfo) - err := q.WhereEqual("id", pid).Find(pinfo) - - return pinfo, err -} - -// GetGroupPkgInfoById returns group of package infomration by pid in order to reduce database connect times. -// The formatted pid looks like '$|', so we need to cut '$' here. -func GetGroupPkgInfoById(pids []string) ([]*PkgInfo, error) { - // Connect to database. - q := connDb() - defer q.Db.Close() - - pinfos := make([]*PkgInfo, 0, len(pids)) - for _, v := range pids { - if len(v) > 1 { - pid, err := strconv.Atoi(v[1:]) - if err == nil { - pinfo := new(PkgInfo) - err = q.WhereEqual("id", pid).Find(pinfo) - if err == nil { - pinfos = append(pinfos, pinfo) - } - } - } - } - return pinfos, nil -} - -// DeleteProject deletes everything about the path in database, and update import information. -func DeleteProject(path string) error { - // Check path length to reduce connect times. (except launchpad.net) - if path[0] != 'l' && len(strings.Split(path, "/")) <= 2 { - return errors.New("models.DeleteProject(): Short path as not needed.") - } - - // Connect to database. - q := connDb() - defer q.Db.Close() - - var i1 int64 - // Delete package information. - info := new(PkgInfo) - err := q.WhereEqual("path", path).Find(info) - if err == nil { - i1, err = q.Delete(info) - if err != nil { - //beego.Error("models.DeleteProject(): Information:", err) - } - } - - if i1 > 0 { - //beego.Info("models.DeleteProject(", path, i1, ")") - } - - return nil -} - -// SearchDoc returns packages information that contain keyword -func SearchDoc(key string) ([]*PkgInfo, error) { - // Connect to database. - q := connDb() - defer q.Db.Close() - - var pkgInfos []*PkgInfo - condition := qbs.NewCondition("path like ?", "%"+key+"%") - err := q.Condition(condition).OrderBy("path").FindAll(&pkgInfos) - return pkgInfos, err -}