aboutsummaryrefslogtreecommitdiff
path: root/ki18n/gettext/gettext.go
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2025-12-30 22:25:59 +0100
committerMartin Kagamino Lehoux <martin@lehoux.net>2025-12-30 22:25:59 +0100
commit43efffb01ca78a8165f63d427de6cffa480a40a0 (patch)
tree458f31c5a7f9421b43cdf161bd70c0a0375959e5 /ki18n/gettext/gettext.go
parentebc163b0b48d74eb7bddb25968c68d958e9087c3 (diff)
Move gettext
Diffstat (limited to 'ki18n/gettext/gettext.go')
-rw-r--r--ki18n/gettext/gettext.go213
1 files changed, 0 insertions, 213 deletions
diff --git a/ki18n/gettext/gettext.go b/ki18n/gettext/gettext.go
deleted file mode 100644
index dbf82d0..0000000
--- a/ki18n/gettext/gettext.go
+++ /dev/null
@@ -1,213 +0,0 @@
-package main
-
-import (
- "errors"
- "flag"
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "io/fs"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/martinlehoux/kagamigo/kcore"
- "golang.org/x/exp/slog"
- "gopkg.in/yaml.v3"
-)
-
-func isIdentifierRune(char rune) bool {
- return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char == '.'
-}
-
-type ArgsParser struct {
- argsCount int
- currentBlock string
- currentNesting int
- currentStringLiteral bool
-}
-
-func (p *ArgsParser) parseStringLiteral(char rune) {
- switch char {
- case '"':
- p.currentStringLiteral = false
- p.currentBlock = ""
- default:
- p.currentBlock += string(char)
- }
-}
-
-func (p *ArgsParser) parseNonStringLiteral(char rune) {
- if isIdentifierRune(char) {
- p.currentBlock += string(char)
- return
- }
- switch char {
- case ' ':
- if p.currentBlock != "" {
- if p.currentNesting == 0 {
- p.argsCount++
- }
- p.currentBlock = ""
- }
- case '(':
- p.currentBlock = ""
- p.currentNesting++
- case ')':
- p.currentBlock = ""
- p.currentNesting--
- p.argsCount++
- case '"':
- p.currentStringLiteral = true
- }
-}
-
-func (p *ArgsParser) Parse(args string) {
- for _, char := range args {
- switch p.currentStringLiteral {
- case true:
- p.parseStringLiteral(char)
- case false:
- p.parseNonStringLiteral(char)
- }
- }
-}
-
-func (p ArgsParser) ArgsCount() int {
- if p.currentBlock != "" {
- p.argsCount++
- }
- return p.argsCount
-}
-
-func extractKeys(content string) map[string]int {
- extractedKeys := make(map[string]int, 0)
- reg := regexp.MustCompile(`\{ ?((?:login\.)?Tr\("[^"]*"[^}]*\) ?)\}`)
- matches := reg.FindAllStringSubmatch(content, -1)
- for _, match := range matches {
- fakePackage := fmt.Sprintf(`package main
-
- func main() {
- %s
- }
- `, match[1])
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, "", fakePackage, 0)
- kcore.Expect(err, "error parsing file")
- main := f.Decls[0].(*ast.FuncDecl)
- call := main.Body.List[0].(*ast.ExprStmt).X.(*ast.CallExpr)
- isTrCall := false
- switch call.Fun.(type) {
- case *ast.SelectorExpr:
- selector := call.Fun.(*ast.SelectorExpr)
- isTrCall = selector.Sel.Name == "Tr"
-
- case *ast.Ident:
- ident := call.Fun.(*ast.Ident)
- isTrCall = ident.Name == "Tr"
- }
- if isTrCall {
- keyLiteral := call.Args[1].(*ast.BasicLit).Value
- key, err := strconv.Unquote(keyLiteral)
- if err != nil {
- slog.Warn("error unquoting key", "key", keyLiteral)
- key = strings.Trim(keyLiteral, `"`)
- }
- extractedKeys[key] = len(call.Args) - 2
- }
- }
- return extractedKeys
-}
-
-func extractAllKeys() map[string]int {
- extractedKeys := make(map[string]int, 0)
-
- err := filepath.Walk(".", func(path string, info fs.FileInfo, err error) error {
- if !info.IsDir() && filepath.Ext(path) == ".templ" {
- content, err := os.ReadFile(path) // #nosec G304
- kcore.Expect(err, "error reading file")
- for key, value := range extractKeys(string(content)) {
- extractedKeys[key] = value
- }
- }
- return nil
- })
- kcore.Expect(err, "error walking templates directory")
-
- return extractedKeys
-}
-
-func getOrCreateLocale(lang string, logger *slog.Logger) map[string]string {
- currentLocales := make(map[string]string, 0)
- locales, err := os.ReadFile(filepath.Join("locales", lang, "index.yml")) // #nosec G304
- if errors.Is(err, os.ErrNotExist) {
- logger.Info("no locales file found, creating")
- err = os.MkdirAll(filepath.Join("locales", lang), 0o700)
- kcore.Expect(err, "error creating directory")
- err = os.WriteFile(filepath.Join("locales", lang, "index.yml"), []byte{}, 0o600)
- kcore.Expect(err, "error writing file")
- locales, err = os.ReadFile(filepath.Join("locales", lang, "index.yml")) // #nosec G304
- kcore.Expect(err, "error reading file")
- } else {
- kcore.Expect(err, "error reading file")
- }
- kcore.Expect(yaml.Unmarshal(locales, &currentLocales), "error unmarshalling yaml")
- return currentLocales
-}
-
-func main() {
- write := flag.Bool("write", false, "write new locales")
- flag.Parse()
- baseLogger := slog.Default()
- langs := [...]string{"en-GB", "fr-FR"}
-
- extractedKeys := extractAllKeys()
- baseLogger.Info("extracted keys from templates", slog.Int("count", len(extractedKeys)))
-
- for _, lang := range langs {
- logger := baseLogger.With(slog.String("lang", lang))
- currentLocales := getOrCreateLocale(lang, logger)
- newLocales := make(map[string]string, 0)
-
- correctLocales := 0
- for key, translation := range currentLocales {
- currentArgsCount := strings.Count(translation, "%")
- expectedArgsCount, ok := extractedKeys[key]
- switch {
- case !ok:
- logger.Info(`found unused key`, slog.String("key", key))
- case currentArgsCount != expectedArgsCount:
- logger.Info(`found translation with incorrect number of arguments`, slog.String("key", key), slog.Int("current", currentArgsCount), slog.Int("expected", expectedArgsCount))
- newLocales[key] = ""
- case translation == "":
- newLocales[key] = ""
- default:
- newLocales[key] = currentLocales[key]
- correctLocales++
- }
- }
-
- for key := range extractedKeys {
- if _, ok := currentLocales[key]; !ok {
- logger.Info(`found missing key`, slog.String("key", key))
- newLocales[key] = ""
- }
- }
- var completion string
- if len(extractedKeys) > 0 {
- completion = fmt.Sprintf("%d%%", correctLocales*100/len(extractedKeys))
- } else {
- completion = "?%"
- }
- logger.Info("finished checking locales", slog.Int("count", len(newLocales)), slog.Int("correct", correctLocales), slog.String("completion", completion))
-
- if *write {
- content, err := yaml.Marshal(newLocales)
- kcore.Expect(err, "error marshalling yaml")
- kcore.Expect(os.WriteFile(filepath.Join("locales", lang, "index.yml"), content, 0o600), "error writing file")
- }
- }
-}