From 9d10a5740ec6d3040a4217afbc3dab65f8547098 Mon Sep 17 00:00:00 2001 From: MJacred Date: Fri, 10 Jan 2020 13:51:53 +0100 Subject: [PATCH] prettify language source file, wip #210 --- v2/goi18n/extract_command.go | 13 ++++++++++++- v2/goi18n/main.go | 35 +++++++++++++++++++++++++++++++++++ v2/goi18n/marshal.go | 13 +++++++------ v2/goi18n/merge_command.go | 33 ++++++++++++++++++++++++--------- v2/i18n/message.go | 2 +- v2/i18n/parse.go | 1 + 6 files changed, 80 insertions(+), 17 deletions(-) diff --git a/v2/goi18n/extract_command.go b/v2/goi18n/extract_command.go index 9f46160d..73d42706 100644 --- a/v2/goi18n/extract_command.go +++ b/v2/goi18n/extract_command.go @@ -25,6 +25,15 @@ If no files or paths are provided, it walks the current working directory. Flags: + -pretty prettyLevel + In case you write the source language file yourself, but don't adhere strictly to the file format. + This command flag will prettify your source language localization. + Allowed values: + - [empty] + - lean + - full + Default: [empty] + -sourceLanguage tag The language tag of the extracted messages (e.g. en, en-US, zh-Hant-CN). Default: en @@ -45,6 +54,7 @@ type extractCommand struct { sourceLanguage languageTag outdir string format string + prettyLevel pretty } func (ec *extractCommand) name() string { @@ -55,6 +65,7 @@ func (ec *extractCommand) parse(args []string) error { flags := flag.NewFlagSet("extract", flag.ExitOnError) flags.Usage = usageExtract + flags.Var(&ec.prettyLevel, "pretty", "") flags.Var(&ec.sourceLanguage, "sourceLanguage", "en") flags.StringVar(&ec.outdir, "outdir", ".", "") flags.StringVar(&ec.format, "format", "toml", "") @@ -108,7 +119,7 @@ func (ec *extractCommand) execute() error { messageTemplates[m.ID] = mt } } - path, content, err := writeFile(ec.outdir, "active", ec.sourceLanguage.Tag(), ec.format, messageTemplates, true) + path, content, err := writeFile(ec.outdir, "active", ec.sourceLanguage.Tag(), ec.format, messageTemplates, ec.prettyLevel, true) if err != nil { return err } diff --git a/v2/goi18n/main.go b/v2/goi18n/main.go index f2f3d2c1..abee5a60 100644 --- a/v2/goi18n/main.go +++ b/v2/goi18n/main.go @@ -153,3 +153,38 @@ func (lt languageTag) Tag() language.Tag { } return tag } + +const ( + // Lean prints key-value-pairs in active language source file with table + Lean int8 = 1 << iota + // Full means the hash will be printed additionally to `Lean` to the active language source file as well + Full +) + +type pretty struct{ + Level int8 +} + +func (p pretty) String() string { + switch p.Level { + case Lean: + return "lean" + case Full: + return "full" + } + return "undefined" +} + +func (p *pretty) Set(value string) error { + lean := pretty{Lean} + full := pretty{Full} + + if value == lean.String() { + *p = lean + } else if value == full.String() { + *p = full + } else { + return fmt.Errorf("invalid value %q for pretty flag", value) + } + return nil +} diff --git a/v2/goi18n/marshal.go b/v2/goi18n/marshal.go index 751b698a..46dab18a 100644 --- a/v2/goi18n/marshal.go +++ b/v2/goi18n/marshal.go @@ -13,8 +13,8 @@ import ( yaml "gopkg.in/yaml.v2" ) -func writeFile(outdir, label string, langTag language.Tag, format string, messageTemplates map[string]*i18n.MessageTemplate, sourceLanguage bool) (path string, content []byte, err error) { - v := marshalValue(messageTemplates, sourceLanguage) +func writeFile(outdir, label string, langTag language.Tag, format string, messageTemplates map[string]*i18n.MessageTemplate, prettyLevel pretty, sourceLanguage bool) (path string, content []byte, err error) { + v := marshalValue(messageTemplates, prettyLevel, sourceLanguage) content, err = marshal(v, format) if err != nil { return "", nil, fmt.Errorf("failed to marshal %s strings to %s: %s", langTag, format, err) @@ -23,18 +23,19 @@ func writeFile(outdir, label string, langTag language.Tag, format string, messag return } -func marshalValue(messageTemplates map[string]*i18n.MessageTemplate, sourceLanguage bool) interface{} { +func marshalValue(messageTemplates map[string]*i18n.MessageTemplate, prettyLevel pretty, sourceLanguage bool) interface{} { v := make(map[string]interface{}, len(messageTemplates)) for id, template := range messageTemplates { - if other := template.PluralTemplates[plural.Other]; sourceLanguage && len(template.PluralTemplates) == 1 && - other != nil && template.Description == "" && template.LeftDelim == "" && template.RightDelim == "" { + if other := template.PluralTemplates[plural.Other]; prettyLevel == (pretty{}) && sourceLanguage && + len(template.PluralTemplates) == 1 && other != nil && template.Description == "" && + template.LeftDelim == "" && template.RightDelim == "" { v[id] = other.Src } else { m := map[string]string{} if template.Description != "" { m["description"] = template.Description } - if !sourceLanguage { + if !sourceLanguage || (sourceLanguage && prettyLevel != (pretty{}) && prettyLevel.Level == Full) { m["hash"] = template.Hash } for pluralForm, template := range template.PluralTemplates { diff --git a/v2/goi18n/merge_command.go b/v2/goi18n/merge_command.go index cc66a21e..d5469fd2 100644 --- a/v2/goi18n/merge_command.go +++ b/v2/goi18n/merge_command.go @@ -36,6 +36,15 @@ appropriate name and pass it in to goi18n merge. Flags: + -pretty prettyLevel + In case you write the source language file yourself, but don't adhere strictly to the file format. + This command flag will prettify your source language localization. + Allowed values: + - [empty] + - lean + - full + Default: [empty] + -sourceLanguage tag Translate messages from this language (e.g. en, en-US, zh-Hant-CN) Default: en @@ -56,6 +65,7 @@ type mergeCommand struct { sourceLanguage languageTag outdir string format string + prettyLevel pretty } func (mc *mergeCommand) name() string { @@ -66,6 +76,7 @@ func (mc *mergeCommand) parse(args []string) error { flags := flag.NewFlagSet("merge", flag.ExitOnError) flags.Usage = usageMerge + flags.Var(&mc.prettyLevel, "pretty", "") flags.Var(&mc.sourceLanguage, "sourceLanguage", "en") flags.StringVar(&mc.outdir, "outdir", ".", "") flags.StringVar(&mc.format, "format", "toml", "") @@ -89,7 +100,8 @@ func (mc *mergeCommand) execute() error { } inFiles[path] = content } - ops, err := merge(inFiles, mc.sourceLanguage.Tag(), mc.outdir, mc.format) + + ops, err := merge(inFiles, mc.prettyLevel, mc.sourceLanguage.Tag(), mc.outdir, mc.format) if err != nil { return err } @@ -110,7 +122,7 @@ type fileSystemOp struct { deleteFiles []string } -func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdir, outputFormat string) (*fileSystemOp, error) { +func merge(messageFiles map[string][]byte, prettyLevel pretty, sourceLanguageTag language.Tag, outdir, outputFormat string) (*fileSystemOp, error) { unmerged := make(map[language.Tag][]map[string]*i18n.MessageTemplate) sourceMessageTemplates := make(map[string]*i18n.MessageTemplate) unmarshalFuncs := map[string]i18n.UnmarshalFunc{ @@ -118,10 +130,11 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi "toml": toml.Unmarshal, "yaml": yaml.Unmarshal, } + // Generate message templates from source and target files. for path, content := range messageFiles { mf, err := i18n.ParseMessageFileBytes(content, path, unmarshalFuncs) if err != nil { - return nil, fmt.Errorf("failed to load message file %s: %s", path, err) + return nil, fmt.Errorf("failed to load message file %q: %s", path, err) } templates := map[string]*i18n.MessageTemplate{} for _, m := range mf.Messages { @@ -145,14 +158,16 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi } if len(sourceMessageTemplates) == 0 { - return nil, fmt.Errorf("no messages found for source locale %s", sourceLanguageTag) + return nil, fmt.Errorf("no messages found for source locale %q", sourceLanguageTag) } + // Join the lists elements per language tag in `unmerged` by language into `all` and + // add already translated message templates. pluralRules := plural.DefaultRules() all := make(map[language.Tag]map[string]*i18n.MessageTemplate) all[sourceLanguageTag] = sourceMessageTemplates for _, srcTemplate := range sourceMessageTemplates { - for dstLangTag, messageTemplates := range unmerged { + for dstLangTag, dstMessageTemplatesList := range unmerged { if dstLangTag == sourceLanguageTag { continue } @@ -179,8 +194,8 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi } // Check all unmerged message templates for this message id. - for _, messageTemplates := range messageTemplates { - unmergedTemplate := messageTemplates[srcTemplate.ID] + for _, dstMessageTemplates := range dstMessageTemplatesList { + unmergedTemplate := dstMessageTemplates[srcTemplate.ID] if unmergedTemplate == nil { continue } @@ -232,7 +247,7 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi writeFiles := make(map[string][]byte, len(translate)+len(active)) for langTag, messageTemplates := range translate { - path, content, err := writeFile(outdir, "translate", langTag, outputFormat, messageTemplates, false) + path, content, err := writeFile(outdir, "translate", langTag, outputFormat, messageTemplates, prettyLevel, false) if err != nil { return nil, err } @@ -240,7 +255,7 @@ func merge(messageFiles map[string][]byte, sourceLanguageTag language.Tag, outdi } deleteFiles := []string{} for langTag, messageTemplates := range active { - path, content, err := writeFile(outdir, "active", langTag, outputFormat, messageTemplates, langTag == sourceLanguageTag) + path, content, err := writeFile(outdir, "active", langTag, outputFormat, messageTemplates, prettyLevel, langTag == sourceLanguageTag) if err != nil { return nil, err } diff --git a/v2/i18n/message.go b/v2/i18n/message.go index f8f789a5..73cd2f6d 100644 --- a/v2/i18n/message.go +++ b/v2/i18n/message.go @@ -21,7 +21,7 @@ type Message struct { // LeftDelim is the left Go template delimiter. LeftDelim string - // RightDelim is the right Go template delimiter.`` + // RightDelim is the right Go template delimiter. RightDelim string // Zero is the content of the message for the CLDR plural form "zero". diff --git a/v2/i18n/parse.go b/v2/i18n/parse.go index 57dd7fe7..76af5f33 100644 --- a/v2/i18n/parse.go +++ b/v2/i18n/parse.go @@ -138,6 +138,7 @@ func addChildMessages(id string, data interface{}, messages []*Message) ([]*Mess return messages, nil } +// parsePath extracts and returns the language tag and file format used for unmarshalling from the given path. func parsePath(path string) (langTag, format string) { formatStartIdx := -1 for i := len(path) - 1; i >= 0; i-- {