penguin/golifehk

@golifehk bot in Telegram

cmd/compilei18n/main.go

raw ยท 2223 bytes

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
}