diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/extpo/main.go (renamed from cmd/exttmpl/main.go) | 116 | ||||
-rw-r--r-- | cmd/extwiki/main.go | 151 |
2 files changed, 222 insertions, 45 deletions
diff --git a/cmd/exttmpl/main.go b/cmd/extpo/main.go index 9017ab8..6aa1503 100644 --- a/cmd/exttmpl/main.go +++ b/cmd/extpo/main.go @@ -10,6 +10,7 @@ import ( "slices" "strings" "text/template/parse" + "time" ) type config struct { @@ -22,7 +23,7 @@ type config struct { type translation struct { msgid string msgidPlural string - msgctx string + msgctxt string domain string } @@ -41,12 +42,11 @@ func (l loc) String() string { } var ( - outfile *os.File currentFile []byte currentPath string lastComment string - translations map[translation]transinfo = make(map[translation]transinfo) - configs = map[string]config{ + translations = make(map[translation]transinfo) + configs = map[string]config{ "Get": {1, -1, -1, -1}, "GetC": {1, -1, 2, -1}, "GetD": {2, -1, -1, 1}, @@ -76,6 +76,7 @@ func main() { os.Exit(1) } + var outfile *os.File if *outpath == "-" { outfile = os.Stdout } else { @@ -86,8 +87,7 @@ func main() { process(f) } - /* TODO: Use the correct date */ - fmt.Fprint(outfile, `# SOME DESCRIPTIVE TITLE. + fmt.Fprintf(outfile, `# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. @@ -97,7 +97,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-27 20:08+0200\n" +"POT-Creation-Date: %s\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -105,7 +105,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -`) +`, time.Now().Format("2006-01-02 15:04-0700")) for tl, ti := range translations { if ti.comment != "" { @@ -122,8 +122,8 @@ msgstr "" fmt.Fprintln(outfile, "#:", x) } - if tl.msgctx != "" { - writeField(outfile, "msgctx", tl.msgctx) + if tl.msgctxt != "" { + writeField(outfile, "msgctxt", tl.msgctxt) } writeField(outfile, "msgid", tl.msgid) if tl.msgidPlural != "" { @@ -155,58 +155,84 @@ func processNode(node parse.Node) { for _, m := range n.Nodes { processNode(m) } + case *parse.PipeNode: + for _, m := range n.Cmds { + processNode(m) + } + case *parse.TemplateNode: + processNode(n.Pipe) case *parse.IfNode: processBranch(n.BranchNode) case *parse.RangeNode: processBranch(n.BranchNode) case *parse.WithNode: processBranch(n.BranchNode) + case *parse.BranchNode: + processBranch(*n) case *parse.ActionNode: - for _, cmd := range n.Pipe.Cmds { - if len(cmd.Args) == 0 { - continue - } + processNode(n.Pipe) + case *parse.CommandNode: + if len(n.Args) == 0 { + break + } - f, ok := cmd.Args[0].(*parse.FieldNode) - if !ok || len(f.Ident) == 0 { - continue + var funcname string + f, ok := n.Args[0].(*parse.FieldNode) + if !ok || len(f.Ident) == 0 { + ff, ok := n.Args[0].(*parse.VariableNode) + if !ok || len(ff.Ident) == 0 { + fff, ok := n.Args[0].(*parse.IdentifierNode) + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + funcname = fff.Ident + } else { + funcname = ff.Ident[len(ff.Ident)-1] } + } else { + funcname = f.Ident[len(f.Ident)-1] + } - cfg, ok := configs[f.Ident[len(f.Ident)-1]] - if !ok { - continue + cfg, ok := configs[funcname] + if !ok { + for _, pipe := range n.Args { + processNode(pipe) } + break + } - var ( - tl translation - linenr int - ) + var ( + tl translation + linenr int + ) - if sn, ok := cmd.Args[cfg.arg].(*parse.StringNode); ok { - tl.msgid = sn.Text - linenr = getlinenr(sn.Pos.Position()) - } else { - continue - } - if cfg.plural != -1 { - if sn, ok := cmd.Args[cfg.plural].(*parse.StringNode); ok { - tl.msgidPlural = sn.Text - } + if sn, ok := n.Args[cfg.arg].(*parse.StringNode); ok { + tl.msgid = sn.Text + linenr = getlinenr(sn.Pos.Position()) + } else { + break + } + if cfg.plural != -1 { + if sn, ok := n.Args[cfg.plural].(*parse.StringNode); ok { + tl.msgidPlural = sn.Text } - if cfg.context != -1 { - if sn, ok := cmd.Args[cfg.context].(*parse.StringNode); ok { - tl.msgctx = sn.Text - } + } + if cfg.context != -1 { + if sn, ok := n.Args[cfg.context].(*parse.StringNode); ok { + tl.msgctxt = sn.Text } + } - ti := translations[tl] - if lastComment != "" { - ti.comment = lastComment - lastComment = "" - } - ti.locs = append(ti.locs, loc{currentPath, linenr}) - translations[tl] = ti + ti := translations[tl] + if lastComment != "" { + ti.comment = lastComment + lastComment = "" } + ti.locs = append(ti.locs, loc{currentPath, linenr}) + translations[tl] = ti case *parse.CommentNode: if strings.HasPrefix(n.Text, "/* TRANSLATORS:") { lastComment = strings.TrimSpace(n.Text[2 : len(n.Text)-2]) diff --git a/cmd/extwiki/main.go b/cmd/extwiki/main.go new file mode 100644 index 0000000..76e0f3a --- /dev/null +++ b/cmd/extwiki/main.go @@ -0,0 +1,151 @@ +package main + +import ( + "flag" + "fmt" + "maps" + "os" + "path/filepath" + "slices" + "sort" + "text/template/parse" +) + +var ( + titles = make(map[string]struct{}) + configs = map[string]int{"Wikipedia": 1} +) + +func usage() { + fmt.Fprintf(os.Stderr, "Usage: %s [flags] template...\n", + filepath.Base(os.Args[0])) + flag.PrintDefaults() +} + +func main() { + try(os.Chdir(filepath.Dir(os.Args[0]))) + + outpath := flag.String("out", "-", "output file") + flag.Usage = usage + flag.Parse() + + if flag.NArg() < 1 { + flag.Usage() + os.Exit(1) + } + + var outfile *os.File + if *outpath == "-" { + outfile = os.Stdout + } else { + outfile = try2(os.Create(*outpath)) + } + + for _, f := range flag.Args() { + process(f) + } + + fmt.Fprint(outfile, `// Code generated by extwiki. DO NOT EDIT. + +package wikipedia + +var extractedTitles = [...]string{ +`) + xs := slices.Collect(maps.Keys(titles)) + sort.Strings(xs) + for _, t := range xs { + fmt.Fprintf(outfile, "%q,\n", t) + } + fmt.Fprint(outfile, "}\n") +} + +func process(path string) { + currentFile := try2(os.ReadFile(path)) + trees := make(map[string]*parse.Tree) + t := parse.New(path) + t.Mode |= parse.ParseComments | parse.SkipFuncCheck + try2(t.Parse(string(currentFile), "", "", trees)) + for _, t := range trees { + processNode(t.Root) + } +} + +func processNode(node parse.Node) { + switch n := node.(type) { + case *parse.ListNode: + for _, m := range n.Nodes { + processNode(m) + } + case *parse.PipeNode: + for _, m := range n.Cmds { + processNode(m) + } + case *parse.TemplateNode: + processNode(n.Pipe) + case *parse.IfNode: + processBranch(n.BranchNode) + case *parse.RangeNode: + processBranch(n.BranchNode) + case *parse.WithNode: + processBranch(n.BranchNode) + case *parse.BranchNode: + processBranch(*n) + case *parse.ActionNode: + processNode(n.Pipe) + case *parse.CommandNode: + if len(n.Args) == 0 { + break + } + + var funcname string + f, ok := n.Args[0].(*parse.FieldNode) + if !ok || len(f.Ident) == 0 { + ff, ok := n.Args[0].(*parse.VariableNode) + if !ok || len(ff.Ident) == 0 { + fff, ok := n.Args[0].(*parse.IdentifierNode) + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + funcname = fff.Ident + } else { + funcname = ff.Ident[len(ff.Ident)-1] + } + } else { + funcname = f.Ident[len(f.Ident)-1] + } + + i, ok := configs[funcname] + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + + if sn, ok := n.Args[i].(*parse.StringNode); ok { + titles[sn.Text] = struct{}{} + } + } +} + +func processBranch(n parse.BranchNode) { + processNode(n.List) + if n.ElseList != nil { + processNode(n.ElseList) + } +} + +func try(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err) + os.Exit(1) + } +} + +func try2[T any](val T, err error) T { + try(err) + return val +} |