// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . package main import ( "encoding/json" "fmt" "github.com/TeaOSLab/EdgeCommon/pkg/langs" "github.com/iwind/TeaGo/Tea" _ "github.com/iwind/TeaGo/bootstrap" "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/types" "go/format" "os" "path/filepath" "regexp" "sort" "strconv" "strings" ) func main() { var args = os.Args if len(args) >= 2 { switch args[1] { case "generate": runGenerate() } } else { fmt.Println("Usage: langs [generate]") } } func runGenerate() { var rootDir = filepath.Clean(Tea.Root + "/../pkg/langs/protos") dir, err := os.Open(rootDir) if err != nil { fmt.Println("[ERROR]read dir failed: " + err.Error()) return } defer func() { _ = dir.Close() }() files, err := dir.Readdir(0) if err != nil { fmt.Println("[ERROR]read dir failed: " + err.Error()) return } var dirRegexp = regexp.MustCompile(`^[a-z]+-[a-z]+$`) var jsonFileNameRegexp = regexp.MustCompile(`^([a-zA-Z0-9]+)(_([a-zA-Z0-9]+))+\.json$`) var messageCodeRegexp = regexp.MustCompile(`^[a-zA-Z0-9_]+$`) var messageCodes = []string{} var messageMaps = map[string]map[string]string{} // lang => { K:V } var defaultLang = langs.DefaultManager().DefaultLang() for _, file := range files { var dirName = file.Name() if !file.IsDir() || !dirRegexp.MatchString(dirName) { continue } var langCode = dirName var isBaseLang = langCode == defaultLang func() { jsonFiles, err := filepath.Glob(rootDir + "/" + dirName + "/*.json") if err != nil { fmt.Println("[ERROR]list json files failed: " + err.Error()) return } for _, jsonFile := range jsonFiles { var jsonFileName = filepath.Base(jsonFile) if len(jsonFileName) == 0 || !jsonFileNameRegexp.MatchString(jsonFileName) { continue } var module = strings.TrimSuffix(jsonFileName, ".json") data, err := os.ReadFile(jsonFile) if err != nil { fmt.Println("[ERROR]read json file '" + jsonFile + "' failed: " + err.Error()) return } var m = map[string]string{} // code => value err = json.Unmarshal(data, &m) if err != nil { fmt.Println("[ERROR]decode json file '" + jsonFile + "' failed: " + err.Error()) return } var newM = map[string]string{} for code, value := range m { if !messageCodeRegexp.MatchString(code) { fmt.Println("[ERROR]invalid message code '" + code + "'") return } var fullCode = module + "_" + code if isBaseLang { messageCodes = append(messageCodes, fullCode) } newM[fullCode] = value } finalMap, ok := messageMaps[langCode] if !ok { finalMap = map[string]string{} messageMaps[langCode] = finalMap } for code, value := range newM { finalMap[code] = value } } }() } // check message codes fmt.Println("checking message codes ...") var defaultMessageMap = map[string]string{} for langCode, messageMap := range messageMaps { if langCode == defaultLang { // only check lang not equal to 'en-us' defaultMessageMap = messageMap continue } for messageCode := range messageMap { if !lists.ContainsString(messageCodes, messageCode) { fmt.Println("[ERROR]message code '" + messageCode + "' in lang '" + langCode + "' not exist in default lang file ('" + defaultLang + "')") return } } } fmt.Println("found '" + types.String(len(messageCodes)) + "' message codes") // generate codes/codes.go sort.Strings(messageCodes) var codesSource = ` // generated by run 'langs generate' package codes import( "github.com/TeaOSLab/EdgeCommon/pkg/langs" ) const ( ` for _, messageCode := range messageCodes { // add comment to message code comment, _, _ := strings.Cut(defaultMessageMap[messageCode], "\n") codesSource += upperWords(messageCode) + " langs.MessageCode = " + strconv.Quote(messageCode) + " // " + comment + "\n" } codesSource += ` ) ` formattedCodesSource, err := format.Source([]byte(codesSource)) if err != nil { fmt.Println("[ERROR]format 'codes.go' failed: " + err.Error()) return } fmt.Println("generating 'codes/codes.go' ...") err = os.WriteFile(Tea.Root+"/../pkg/langs/codes/codes.go", formattedCodesSource, 0666) if err != nil { fmt.Println("[ERROR]write to 'codes.go' failed: " + err.Error()) return } // generate messages_LANG.go for langCode, messageMap := range messageMaps { var langFile = strings.ReplaceAll("messages/messages_"+langCode+".go", "-", "_") fmt.Println("generating '" + langFile + "' ...") var source = ` // generated by run 'langs generate' package messages import( "github.com/TeaOSLab/EdgeCommon/pkg/langs" ) func init() { langs.Load("` + langCode + `", map[string]string{ ` for code, value := range messageMap { source += strconv.Quote(code) + ": " + strconv.Quote(value) + ",\n" } source += ` }) } ` formattedSource, err := format.Source([]byte(source)) if err != nil { fmt.Println("[ERROR]format '" + langFile + "' failed: " + err.Error()) return } err = os.WriteFile(Tea.Root+"/../pkg/langs/"+langFile, formattedSource, 0666) if err != nil { fmt.Println("[ERROR]write file '" + langFile + "' failed: " + err.Error()) return } } // generate language javascript files for EdgeAdmin for lang, messageMap := range messageMaps { if lang != defaultLang { // TODO merge messageMap with default message map } for _, component := range []string{"EdgeAdmin", "EdgeUser"} { fmt.Println("generating '" + lang + ".js' for " + component + " ...") var targetFile = filepath.Clean(Tea.Root + "/../../" + component + "/web/public/js/langs/" + lang + ".js") var targetDir = filepath.Dir(targetFile) dirStat, _ := os.Stat(targetDir) if dirStat != nil { messageMapJSON, err := json.Marshal(messageMap) if err != nil { fmt.Println("[ERROR]marshal message map failed: " + err.Error()) return } err = os.WriteFile(targetFile, []byte(`// generated by 'langs generate' window.LANG_MESSAGES = `+string(messageMapJSON)+";\n"), 0666) if err != nil { fmt.Println("[ERROR]write file '" + targetFile + "' failed: " + err.Error()) return } } } } fmt.Println("success") } func upperWords(s string) string { var words = strings.Split(s, "_") for index, word := range words { words[index] = upperWord(word) } return strings.Join(words, "") } func upperWord(word string) string { var l = len(word) if l == 0 { return "" } if l == 1 { return strings.ToUpper(word) } // special words switch word { case "api", "http", "https", "tcp", "udp", "ip", "dns", "ns", "waf", "acme", "ssh", "toa": return strings.ToUpper(word) case "ipv6": return "IPv6" case "ddos": return "DDoS" } return strings.ToUpper(word[:1]) + word[1:] }