See toolshed/abra#647
This commit is contained in:
13
.drone.yml
Normal file
13
.drone.yml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
kind: pipeline
|
||||
name: coopcloud.tech/xgettext-go
|
||||
steps:
|
||||
- name: unit tests
|
||||
image: golang:1.25
|
||||
commands:
|
||||
- go test -v ./...
|
||||
|
||||
trigger:
|
||||
action:
|
||||
exclude:
|
||||
- synchronized
|
@ -1,4 +1,6 @@
|
||||
# xgettext-go
|
||||
|
||||
[](https://build.coopcloud.tech/toolshed/xgettext-go)
|
||||
|
||||
* [Forked from here](https://github.com/canonical/snapd/tree/master/i18n/xgettext-go)
|
||||
* [The reason why](https://git.coopcloud.tech/toolshed/abra/issues/647)
|
||||
|
131
main.go
131
main.go
@ -20,6 +20,7 @@ import (
|
||||
type msgID struct {
|
||||
msgidPlural string
|
||||
comment string
|
||||
context string
|
||||
fname string
|
||||
line int
|
||||
formatHint string
|
||||
@ -102,16 +103,27 @@ func inspectNodeForTranslations(fset *token.FileSet, f *ast.File, n ast.Node) bo
|
||||
gettextSelectorPlural := l[0]
|
||||
gettextFuncNamePlural := l[1]
|
||||
|
||||
l = strings.Split(opts.KeywordContext, ".")
|
||||
gettextSelectorContext := l[0]
|
||||
gettextFuncNameContext := l[1]
|
||||
|
||||
switch x := n.(type) {
|
||||
case *ast.CallExpr:
|
||||
if sel, ok := x.Fun.(*ast.SelectorExpr); ok {
|
||||
i18nStr := ""
|
||||
i18nStrPlural := ""
|
||||
i18nStrContext := ""
|
||||
|
||||
if sel.Sel.Name == gettextFuncNamePlural && sel.X.(*ast.Ident).Name == gettextSelectorPlural {
|
||||
i18nStr = x.Args[0].(*ast.BasicLit).Value
|
||||
i18nStrPlural = x.Args[1].(*ast.BasicLit).Value
|
||||
}
|
||||
|
||||
if sel.Sel.Name == gettextFuncNameContext && sel.X.(*ast.Ident).Name == gettextSelectorContext {
|
||||
i18nStr = x.Args[0].(*ast.BasicLit).Value
|
||||
i18nStrContext = x.Args[1].(*ast.BasicLit).Value
|
||||
}
|
||||
|
||||
if sel.Sel.Name == gettextFuncName && sel.X.(*ast.Ident).Name == gettextSelector {
|
||||
i18nStr = constructValue(x.Args[0])
|
||||
}
|
||||
@ -144,6 +156,7 @@ func inspectNodeForTranslations(fset *token.FileSet, f *ast.File, n ast.Node) bo
|
||||
msgIDs[msgidStr] = append(msgIDs[msgidStr], msgID{
|
||||
formatHint: formatHint,
|
||||
msgidPlural: formatI18nStr(i18nStrPlural),
|
||||
context: formatI18nStr(i18nStrContext),
|
||||
fname: posCall.Filename,
|
||||
line: posCall.Line,
|
||||
comment: findCommentsForTranslation(fset, f, posCall),
|
||||
@ -219,6 +232,79 @@ func mustFprintf(w io.Writer, format string, a ...any) {
|
||||
}
|
||||
}
|
||||
|
||||
func formatOutput(in string) string {
|
||||
// split string with \n into multiple lines
|
||||
// to make the output nicer
|
||||
out := strings.Replace(in, "\\n", "\\n\"\n \"", -1)
|
||||
// cleanup too aggressive splitting (empty "" lines)
|
||||
return strings.TrimSuffix(out, "\"\n \"")
|
||||
}
|
||||
|
||||
func writeMsg(out io.Writer, k string, msgidList []msgID) {
|
||||
for _, msgid := range msgidList {
|
||||
if opts.AddComments || opts.AddCommentsTag != "" {
|
||||
mustFprintf(out, "%s", msgid.comment)
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.NoLocation {
|
||||
mustFprintf(out, "#:")
|
||||
for _, msgid := range msgidList {
|
||||
mustFprintf(out, " %s:%d", msgid.fname, msgid.line)
|
||||
}
|
||||
mustFprintf(out, "\n")
|
||||
}
|
||||
|
||||
msgid := msgidList[0]
|
||||
if msgid.formatHint != "" {
|
||||
mustFprintf(out, "#, %s\n", msgid.formatHint)
|
||||
}
|
||||
|
||||
mustFprintf(out, "msgid \"%v\"\n", formatOutput(k))
|
||||
|
||||
if msgid.msgidPlural != "" {
|
||||
mustFprintf(out, "msgid_plural \"%v\"\n", formatOutput(msgid.msgidPlural))
|
||||
mustFprintf(out, "msgstr[0] \"\"\n")
|
||||
mustFprintf(out, "msgstr[1] \"\"\n")
|
||||
} else {
|
||||
mustFprintf(out, "msgstr \"\"\n")
|
||||
}
|
||||
|
||||
mustFprintf(out, "\n")
|
||||
}
|
||||
|
||||
func writeMsgWithContext(out io.Writer, k string, msgidList []msgID) {
|
||||
for _, msgid := range msgidList {
|
||||
if opts.AddComments || opts.AddCommentsTag != "" {
|
||||
mustFprintf(out, "%s", msgid.comment)
|
||||
}
|
||||
|
||||
if !opts.NoLocation {
|
||||
mustFprintf(out, "#: %s:%d\n", msgid.fname, msgid.line)
|
||||
}
|
||||
|
||||
if msgid.formatHint != "" {
|
||||
mustFprintf(out, "#, %s\n", msgid.formatHint)
|
||||
}
|
||||
|
||||
if msgid.context != "" {
|
||||
mustFprintf(out, "msgctxt \"%v\"\n", formatOutput(msgid.context))
|
||||
}
|
||||
|
||||
mustFprintf(out, "msgid \"%v\"\n", formatOutput(k))
|
||||
|
||||
if msgid.msgidPlural != "" {
|
||||
mustFprintf(out, "msgid_plural \"%v\"\n", formatOutput(msgid.msgidPlural))
|
||||
mustFprintf(out, "msgstr[0] \"\"\n")
|
||||
mustFprintf(out, "msgstr[1] \"\"\n")
|
||||
} else {
|
||||
mustFprintf(out, "msgstr \"\"\n")
|
||||
}
|
||||
|
||||
mustFprintf(out, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func writePotFile(out io.Writer) {
|
||||
|
||||
header := fmt.Sprintf(`# SOME DESCRIPTIVE TITLE.
|
||||
@ -253,39 +339,25 @@ msgstr "Project-Id-Version: %s\n"
|
||||
|
||||
// FIXME: use template here?
|
||||
for _, k := range sortedKeys {
|
||||
var msgIDsWithoutContext []msgID
|
||||
var msgIDsWithContext []msgID
|
||||
|
||||
msgidList := msgIDs[k]
|
||||
for _, msgid := range msgidList {
|
||||
if opts.AddComments || opts.AddCommentsTag != "" {
|
||||
mustFprintf(out, "%s", msgid.comment)
|
||||
if msgid.context == "" {
|
||||
msgIDsWithoutContext = append(msgIDsWithoutContext, msgid)
|
||||
} else {
|
||||
msgIDsWithContext = append(msgIDsWithContext, msgid)
|
||||
}
|
||||
}
|
||||
if !opts.NoLocation {
|
||||
mustFprintf(out, "#:")
|
||||
for _, msgid := range msgidList {
|
||||
mustFprintf(out, " %s:%d", msgid.fname, msgid.line)
|
||||
}
|
||||
mustFprintf(out, "\n")
|
||||
|
||||
if len(msgIDsWithoutContext) > 0 {
|
||||
writeMsg(out, k, msgIDsWithoutContext)
|
||||
}
|
||||
msgid := msgidList[0]
|
||||
if msgid.formatHint != "" {
|
||||
mustFprintf(out, "#, %s\n", msgid.formatHint)
|
||||
|
||||
if len(msgIDsWithContext) > 0 {
|
||||
writeMsgWithContext(out, k, msgIDsWithContext)
|
||||
}
|
||||
var formatOutput = func(in string) string {
|
||||
// split string with \n into multiple lines
|
||||
// to make the output nicer
|
||||
out := strings.Replace(in, "\\n", "\\n\"\n \"", -1)
|
||||
// cleanup too aggressive splitting (empty "" lines)
|
||||
return strings.TrimSuffix(out, "\"\n \"")
|
||||
}
|
||||
mustFprintf(out, "msgid \"%v\"\n", formatOutput(k))
|
||||
if msgid.msgidPlural != "" {
|
||||
mustFprintf(out, "msgid_plural \"%v\"\n", formatOutput(msgid.msgidPlural))
|
||||
mustFprintf(out, "msgstr[0] \"\"\n")
|
||||
mustFprintf(out, "msgstr[1] \"\"\n")
|
||||
} else {
|
||||
mustFprintf(out, "msgstr \"\"\n")
|
||||
}
|
||||
mustFprintf(out, "\n")
|
||||
}
|
||||
|
||||
}
|
||||
@ -310,8 +382,9 @@ var opts struct {
|
||||
|
||||
PackageName string `long:"package-name" description:"set package name in output"`
|
||||
|
||||
Keyword string `short:"k" long:"keyword" default:"gettext.Gettext" description:"look for WORD as the keyword for singular strings"`
|
||||
KeywordPlural string `long:"keyword-plural" default:"gettext.NGettext" description:"look for WORD as the keyword for plural strings"`
|
||||
Keyword string `short:"k" long:"keyword" default:"gettext.Gettext" description:"look for WORD as the keyword for singular strings"`
|
||||
KeywordContext string `long:"keyword-ctx" default:"gettext.PGettext" description:"look for WORD as the keyword for singular strings with context"`
|
||||
KeywordPlural string `long:"keyword-plural" default:"gettext.NGettext" description:"look for WORD as the keyword for plural strings"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
118
main_test.go
118
main_test.go
@ -54,6 +54,7 @@ func (s *xgettextTestSuite) SetUpTest(c *C) {
|
||||
opts.AddCommentsTag = "TRANSLATORS:"
|
||||
opts.Keyword = "i18n.G"
|
||||
opts.KeywordPlural = "i18n.NG"
|
||||
opts.KeywordContext = "i18n.GC"
|
||||
opts.SortOutput = true
|
||||
opts.PackageName = "snappy"
|
||||
opts.MsgIDBugsAddress = "snappy-devel@lists.ubuntu.com"
|
||||
@ -102,6 +103,29 @@ func main() {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *xgettextTestSuite) TestProcessFilesSimpleWithContext(c *C) {
|
||||
fname := makeGoSourceFile(c, []byte(`package main
|
||||
|
||||
func main() {
|
||||
// TRANSLATORS: foo comment
|
||||
i18n.GC("foo", "foo context")
|
||||
}
|
||||
`))
|
||||
err := processFiles([]string{fname})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
c.Assert(msgIDs, DeepEquals, map[string][]msgID{
|
||||
"foo": {
|
||||
{
|
||||
comment: "#. TRANSLATORS: foo comment\n",
|
||||
context: "foo context",
|
||||
fname: fname,
|
||||
line: 5,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *xgettextTestSuite) TestProcessFilesMultiple(c *C) {
|
||||
fname := makeGoSourceFile(c, []byte(`package main
|
||||
|
||||
@ -132,6 +156,38 @@ func main() {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *xgettextTestSuite) TestProcessFilesMultipleWithContext(c *C) {
|
||||
fname := makeGoSourceFile(c, []byte(`package main
|
||||
|
||||
func main() {
|
||||
// TRANSLATORS: foo comment
|
||||
i18n.GC("foo", "foo context")
|
||||
|
||||
// TRANSLATORS: bar comment
|
||||
i18n.GC("foo", "foo context 2")
|
||||
}
|
||||
`))
|
||||
err := processFiles([]string{fname})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
c.Assert(msgIDs, DeepEquals, map[string][]msgID{
|
||||
"foo": {
|
||||
{
|
||||
comment: "#. TRANSLATORS: foo comment\n",
|
||||
context: "foo context",
|
||||
fname: fname,
|
||||
line: 5,
|
||||
},
|
||||
{
|
||||
comment: "#. TRANSLATORS: bar comment\n",
|
||||
context: "foo context 2",
|
||||
fname: fname,
|
||||
line: 8,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const header = `# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
@ -174,6 +230,31 @@ msgstr ""
|
||||
c.Assert(out.String(), Equals, expected)
|
||||
}
|
||||
|
||||
func (s *xgettextTestSuite) TestWriteOutputSimpleWithContext(c *C) {
|
||||
msgIDs = map[string][]msgID{
|
||||
"foo": {
|
||||
{
|
||||
fname: "fname",
|
||||
line: 2,
|
||||
comment: "#. foo\n",
|
||||
context: "foo context",
|
||||
},
|
||||
},
|
||||
}
|
||||
out := bytes.NewBuffer([]byte(""))
|
||||
writePotFile(out)
|
||||
|
||||
expected := fmt.Sprintf(`%s
|
||||
#. foo
|
||||
#: fname:2
|
||||
msgctx "foo context"
|
||||
msgid "foo"
|
||||
msgstr ""
|
||||
|
||||
`, header)
|
||||
c.Assert(out.String(), Equals, expected)
|
||||
}
|
||||
|
||||
func (s *xgettextTestSuite) TestWriteOutputMultiple(c *C) {
|
||||
msgIDs = map[string][]msgID{
|
||||
"foo": {
|
||||
@ -203,6 +284,43 @@ msgstr ""
|
||||
c.Assert(out.String(), Equals, expected)
|
||||
}
|
||||
|
||||
func (s *xgettextTestSuite) TestWriteOutputMultipleWithContext(c *C) {
|
||||
msgIDs = map[string][]msgID{
|
||||
"foo": {
|
||||
{
|
||||
fname: "fname",
|
||||
context: "context1",
|
||||
line: 2,
|
||||
comment: "#. comment1\n",
|
||||
},
|
||||
{
|
||||
fname: "fname",
|
||||
context: "context2",
|
||||
line: 4,
|
||||
comment: "#. comment2\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
out := bytes.NewBuffer([]byte(""))
|
||||
writePotFile(out)
|
||||
|
||||
expected := fmt.Sprintf(`%s
|
||||
#. comment1
|
||||
#: fname:2
|
||||
msgctx "context1"
|
||||
msgid "foo"
|
||||
msgstr ""
|
||||
|
||||
#. comment2
|
||||
#: fname:4
|
||||
msgctx "context2"
|
||||
msgid "foo"
|
||||
msgstr ""
|
||||
|
||||
`, header)
|
||||
c.Assert(out.String(), Equals, expected)
|
||||
}
|
||||
|
||||
func (s *xgettextTestSuite) TestWriteOutputNoComment(c *C) {
|
||||
msgIDs = map[string][]msgID{
|
||||
"foo": {
|
||||
|
Reference in New Issue
Block a user