123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- package main
- import (
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "regexp"
- "strings"
- "text/template"
- "time"
- "code.osinet.fr/fgm/kurz/translations"
- "github.com/nicksnyder/go-i18n/v2/i18n"
- "github.com/spf13/cobra"
- "golang.org/x/text/language"
- "gopkg.in/yaml.v2"
- )
- var cmdGenerateTranslations = &cobra.Command{
- Args: cobra.NoArgs,
- Long: "Generate the translations package",
- Run: generateTranslationsHandler,
- Short: "Converts the messages.*.yaml files to a compilable i18n messages embed package. Only useful during Kurz development",
- Use: "generate-translations",
- }
- func init() {
- cmd.AddCommand(cmdGenerateTranslations)
- }
- /*
- dumpMessages is a debug helper, logging a messageMap in YAML format for readability.
- */
- func dumpMessages(messageMap translations.MessageMap) {
- out, err := yaml.Marshal(messageMap)
- if err != nil {
- panic(err)
- }
- log.Println("Messages:\n" + string(out))
- }
- func generateTranslationsHandler(cmd *cobra.Command, args []string) {
- cwd, err := os.Getwd()
- if err != nil {
- panic(err)
- }
- t0 := time.Now()
- log.Printf("Finding message catalogs below %s\n", cwd)
- tags := []string{
- language.English.String(),
- language.French.String(),
- }
- messageMap := make(translations.MessageMap)
- for _, tag := range tags {
- messageMap[language.Make(tag)] = [](*i18n.Message){}
- }
- // Build the regex matching message file names.
- re := regexp.MustCompile("messages\\.(?:" + strings.Join(tags, "|") + ")\\.yaml")
- scanned := 0
- catalogCount := 0
- walkErr := filepath.Walk(cwd, scanCatalog(messageMap, re, &scanned, &catalogCount))
- if walkErr != nil {
- panic(walkErr)
- }
- t1 := time.Now()
- diff := t1.Sub(t0)
- log.Printf("%d items scanned, %d catalogs loaded in %d msec\n", scanned, catalogCount, diff/time.Millisecond)
- // dumpMessages(messageMap)
- pwd, err := os.Getwd()
- if err != nil {
- panic(err)
- }
- println(pwd)
- translationsDir := pwd + "/translations"
- tpl := template.Must(template.ParseFiles(translationsDir + "/template.go.gotext"))
- gen, err := os.Create(translationsDir + "/generated.go")
- if err != nil {
- panic(err)
- }
- err = tpl.ExecuteTemplate(gen, "template.go.gotext", messageMap)
- if err != nil {
- panic(err)
- }
- }
- /*
- parseMessageFileBytes merges the messages found in "buf" into the messageMap.
- See scanCatalog().
- */
- func parseMessages(messagesMap translations.MessageMap, buf []byte, tag language.Tag) error {
- if len(buf) == 0 {
- return nil
- }
- raw := make(map[string]interface{})
- if err := yaml.Unmarshal(buf, &raw); err != nil {
- return err
- }
- for id, item := range raw {
- switch item.(type) {
- // Degenerate case: "key: one"
- case string:
- message := &i18n.Message{
- ID: id,
- Other: item.(string),
- }
- messagesMap[tag] = append(messagesMap[tag], message)
- // Normal case: key: { ID: ..., one: ... }.
- case map[interface{}]interface{}:
- serial, err := yaml.Marshal(item)
- if err != nil {
- panic(err)
- }
- message := &i18n.Message{
- ID: id,
- }
- err = yaml.Unmarshal(serial, message)
- if err != nil {
- panic(err)
- }
- messagesMap[tag] = append(messagesMap[tag], message)
- default:
- log.Fatalf("Unsupported message format %#v", item)
- }
- }
- return nil
- }
- // parsePath is a copy of go-i18n.internal.parsePath
- //
- // Used under its MIT license.
- func parsePath(path string) (langTag, format string) {
- formatStartIdx := -1
- for i := len(path) - 1; i >= 0; i-- {
- c := path[i]
- if os.IsPathSeparator(c) {
- if formatStartIdx != -1 {
- langTag = path[i+1 : formatStartIdx]
- }
- return
- }
- if path[i] == '.' {
- if formatStartIdx != -1 {
- langTag = path[i+1 : formatStartIdx]
- return
- }
- if formatStartIdx == -1 {
- format = path[i+1:]
- formatStartIdx = i
- }
- }
- }
- if formatStartIdx != -1 {
- langTag = path[:formatStartIdx]
- }
- return
- }
- /*
- scanCatalog builds a WalkFn callback parsing each message file as it is found, and
- merging its values into messageMap.
- See generateTranslationsHandler
- */
- func scanCatalog(messageMap translations.MessageMap, re *regexp.Regexp, scanned *int, catalogCount *int) filepath.WalkFunc {
- return func(path string, info os.FileInfo, err error) error {
- *scanned++
- if err != nil {
- panic(err)
- }
- if info.IsDir() {
- return nil
- }
- if !re.MatchString(info.Name()) {
- return nil
- }
- langid, _ := parsePath(path)
- tag := language.Make(langid)
- _, ok := messageMap[tag]
- if !ok {
- log.Printf("Found message catalog %s in unexpected language %s, skipping.",
- path, tag.String())
- return nil
- }
- buf, err := ioutil.ReadFile(path)
- if err != nil {
- panic(err)
- }
- err = parseMessages(messageMap, buf, tag)
- *catalogCount++
- return err
- }
- }
|