package main import ( "bytes" "encoding/json" "fmt" "go/ast" "go/parser" "go/token" "os" "sort" "strconv" ) func main() { const goFile = "i18n/messages.go" const outFile = "resources/src/messages.json" keys, err := extractStringConsts(goFile) if err != nil { panic(err) } existing := map[string]string{} if data, err := os.ReadFile(outFile); err == nil { if len(bytes.TrimSpace(data)) > 0 { if err := json.Unmarshal(data, &existing); err != nil { panic(fmt.Errorf("parse %s: %w", outFile, err)) } } } else if !os.IsNotExist(err) { panic(err) } merged := map[string]string{} for _, key := range keys { if val, ok := existing[key]; ok { merged[key] = val } else { merged[key] = "TODO: " + key } } // Detect stale keys. for key := range existing { found := false for _, k := range keys { if k == key { found = true break } } if !found { fmt.Fprintf(os.Stderr, "stale key in %s: %s\n", outFile, key) } } // Write stable sorted JSON. sorted := make([]string, 0, len(merged)) for k := range merged { sorted = append(sorted, k) } sort.Strings(sorted) buf := &bytes.Buffer{} buf.WriteString("{\n") for i, k := range sorted { kj, _ := json.Marshal(k) vj, _ := json.Marshal(merged[k]) comma := "," if i == len(sorted)-1 { comma = "" } fmt.Fprintf(buf, " %s: %s%s\n", kj, vj, comma) } buf.WriteString("}\n") if err := os.WriteFile(outFile, buf.Bytes(), 0644); err != nil { panic(err) } } func extractStringConsts(path string) ([]string, error) { fset := token.NewFileSet() file, err := parser.ParseFile(fset, path, nil, 0) if err != nil { return nil, err } var keys []string for _, decl := range file.Decls { gen, ok := decl.(*ast.GenDecl) if !ok || gen.Tok != token.CONST { continue } for _, spec := range gen.Specs { vs, ok := spec.(*ast.ValueSpec) if !ok { continue } for _, value := range vs.Values { lit, ok := value.(*ast.BasicLit) if !ok || lit.Kind != token.STRING { continue } s, err := strconv.Unquote(lit.Value) if err != nil { return nil, err } keys = append(keys, s) } } } sort.Strings(keys) return keys, nil }