Skip to content

Commit 7390e5f

Browse files
committed
refactor: start refactoring the po compiler
1 parent b45b159 commit 7390e5f

7 files changed

Lines changed: 274 additions & 55 deletions

File tree

pkg/po/compile/po_compiler.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ type PoCompiler struct {
2121

2222
nplurals uint // Number of plural forms from the header
2323
header po.Header // Parsed header information
24+
25+
writer io.Writer
2426
}
2527

2628
// error creates and logs an error message if error reporting is enabled.
@@ -94,7 +96,9 @@ func (c *PoCompiler) ToBytesWithOptions(opts ...PoOption) []byte {
9496

9597
// init initializes the compiler by parsing header information.
9698
func (c *PoCompiler) init() {
97-
c.header = c.File.Header()
99+
if c.Config.ManageHeader {
100+
c.header = c.File.Header()
101+
}
98102
c.nplurals = c.header.Nplurals()
99103
}
100104

@@ -121,11 +125,6 @@ func (c PoCompiler) ToWriter(w io.Writer) error {
121125
}
122126
entries := c.File.Entries
123127

124-
// TODO: Remove this later.
125-
if c.Config.CleanDuplicates {
126-
c.info("cleaning duplicates...")
127-
entries = c.File.CutHeader().CleanDuplicates()
128-
}
129128
c.info("writing entries...")
130129

131130
for _, e := range entries {
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package compile
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/Tom5521/gotext-tools/v2/pkg/po"
10+
)
11+
12+
func escapePOString(s string) string {
13+
var buf strings.Builder
14+
for _, r := range s {
15+
switch r {
16+
case '"':
17+
buf.WriteString(`\"`)
18+
case '\\':
19+
buf.WriteString(`\\`)
20+
case '\n':
21+
buf.WriteString(`\n`)
22+
case '\t':
23+
buf.WriteString(`\t`)
24+
case '\r':
25+
buf.WriteString(`\r`)
26+
default:
27+
if strconv.IsPrint(r) {
28+
buf.WriteRune(r)
29+
} else {
30+
fmt.Fprintf(&buf, "\\x%02x", r)
31+
}
32+
}
33+
}
34+
return buf.String()
35+
}
36+
37+
func (c PoCompiler) compileEntries() {
38+
for _, e := range c.File.Entries {
39+
c.entry(e)
40+
}
41+
}
42+
43+
func (c PoCompiler) entry(entry po.Entry) {
44+
eb := EntryBuilder{entry, c.writer, c.Config}
45+
eb.Build()
46+
}
47+
48+
type EntryBuilder struct {
49+
po.Entry
50+
Builder io.Writer
51+
Config PoConfig
52+
}
53+
54+
func (eb EntryBuilder) Build() {
55+
eb.comment()
56+
eb.msgid()
57+
eb.msgstr()
58+
}
59+
60+
func (eb EntryBuilder) header() {}
61+
func (eb EntryBuilder) msgid() {
62+
if eb.HasContext() {
63+
eb.keyword("msgctxt")
64+
eb.string(eb.Context)
65+
}
66+
eb.keyword("msgid")
67+
eb.string(eb.ID)
68+
69+
if eb.IsPlural() {
70+
eb.keyword("msgid_plural")
71+
eb.string(eb.Plural)
72+
}
73+
}
74+
75+
func (eb EntryBuilder) msgstr() {
76+
const format = "msgstr[%d]"
77+
if eb.IsPlural() {
78+
for _, pe := range eb.Plurals {
79+
eb.keyword(fmt.Sprintf(format, pe.ID))
80+
eb.string(pe.Str)
81+
}
82+
return
83+
}
84+
85+
eb.keyword("msgstr")
86+
eb.string(eb.Str)
87+
}
88+
89+
func (eb EntryBuilder) fuzzy() {}
90+
func (eb EntryBuilder) obsolete() {}
91+
func (eb EntryBuilder) translated() {}
92+
func (eb EntryBuilder) untranslated() {}
93+
94+
func (eb EntryBuilder) comment() {
95+
eb.translatorComment()
96+
eb.extractedComment()
97+
eb.referenceComment()
98+
eb.flagComment()
99+
eb.previousComment()
100+
}
101+
102+
func (eb EntryBuilder) translatorComment() {}
103+
func (eb EntryBuilder) extractedComment() {}
104+
105+
func (eb EntryBuilder) referenceComment() {
106+
eb.reference()
107+
}
108+
109+
func (eb EntryBuilder) reference() {}
110+
111+
func (eb EntryBuilder) flagComment() {
112+
eb.flag()
113+
}
114+
115+
func (eb EntryBuilder) flag() {
116+
eb.fuzzyFlag()
117+
}
118+
119+
func (eb EntryBuilder) fuzzyFlag() {}
120+
121+
func (eb EntryBuilder) previousComment() {
122+
eb.previous()
123+
}
124+
125+
func (eb EntryBuilder) previous() {}
126+
127+
func (eb EntryBuilder) keyword(kw string) {
128+
fmt.Fprint(eb.Builder, kw+" ")
129+
}
130+
131+
func (eb EntryBuilder) string(str string) {
132+
if eb.Config.WordWrap {
133+
lines := strings.Split(str, "\n")
134+
for i, line := range lines {
135+
if i != len(lines)-1 {
136+
line += "\n"
137+
}
138+
fmt.Fprint(eb.Builder, `"`)
139+
eb.text(escapePOString(line))
140+
fmt.Fprintln(eb.Builder, `"`)
141+
}
142+
143+
return
144+
}
145+
fmt.Fprint(eb.Builder, `"`)
146+
eb.text(escapePOString(str))
147+
fmt.Fprintln(eb.Builder, `"`)
148+
}
149+
150+
func (eb EntryBuilder) text(txt string) {
151+
fmt.Fprint(eb.Builder, txt)
152+
/*
153+
eb.escapeSequence()
154+
eb.formatDirective()
155+
eb.invalidFormatDirective()
156+
*/
157+
}
158+
159+
func (eb EntryBuilder) escapeSequence() {}
160+
func (eb EntryBuilder) formatDirective() {}
161+
func (eb EntryBuilder) invalidFormatDirective() {}

pkg/po/compile/po_config.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ type PoConfig struct {
4343
ManageHeader bool
4444
HeaderComments bool
4545
HeaderFields bool
46-
CleanDuplicates bool
4746
WordWrap bool
4847
HeaderConfig *po.HeaderConfig
4948

@@ -117,12 +116,6 @@ func PoWithCustomObsoletePrefixRune(r rune) PoOption {
117116
}
118117
}
119118

120-
func PoWithCleanDuplicates(c bool) PoOption {
121-
return func(pc *PoConfig) {
122-
pc.CleanDuplicates = c
123-
}
124-
}
125-
126119
func PoWithManageHeader(b bool) PoOption {
127120
return func(pc *PoConfig) {
128121
pc.ManageHeader = b
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package compile_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/Tom5521/gotext-tools/v2/pkg/po"
8+
"github.com/Tom5521/gotext-tools/v2/pkg/po/compile"
9+
)
10+
11+
func TestCompile(t *testing.T) {
12+
var builder strings.Builder
13+
14+
eb := compile.EntryBuilder{
15+
Entry: po.Entry{
16+
Flags: []string{"plural"},
17+
Comments: []string{"Plural forms for items"},
18+
ExtractedComments: []string{"Shopping cart module"},
19+
Previous: []string{},
20+
Obsolete: false,
21+
ID: "%d item\nlol\nline2",
22+
Context: "shopping_cart",
23+
Plural: "%d items",
24+
Plurals: po.PluralEntries{
25+
{ID: 0, Str: "%d artículo"},
26+
{ID: 1, Str: "%d artículos"},
27+
},
28+
Locations: po.Locations{
29+
{Line: 88, File: "cart.go"},
30+
{Line: 92, File: "cart.go"},
31+
},
32+
},
33+
Builder: &builder,
34+
Config: compile.DefaultPoConfig(
35+
compile.PoWithWordWrap(true),
36+
),
37+
}
38+
39+
eb.Build()
40+
t.Log(builder.String())
41+
}

pkg/po/compile/po_highlight.go

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"fmt"
66
"io"
7-
"regexp"
87

98
"github.com/Tom5521/gotext-tools/v2/internal/util"
109

@@ -142,17 +141,6 @@ func HighlightFromReader(
142141
return highlight(cfg, lex)
143142
}
144143

145-
/* var idTokensMap = map[lexer.TokenType]struct{}{
146-
util.PoSymbols["Msgid"]: {},
147-
util.PoSymbols["Msgctxt"]: {},
148-
util.PoSymbols["Plural"]: {},
149-
}
150-
151-
var strTokensMap = map[lexer.TokenType]struct{}{
152-
util.PoSymbols["Msgstr"]: {},
153-
util.PoSymbols["RB"]: {},
154-
} */
155-
156144
// TODO: Finish this.
157145
func highlight(cfg CSSClassesHighlighting, lex lexer.Lexer) ([]byte, error) {
158146
tokens, err := lexer.ConsumeAll(lex)
@@ -187,33 +175,3 @@ func highlight(cfg CSSClassesHighlighting, lex lexer.Lexer) ([]byte, error) {
187175

188176
return builder.Bytes(), nil
189177
}
190-
191-
var strRegex = regexp.MustCompile(`"(.*)"`)
192-
193-
func colorStrings(tokens []lexer.Token, offset int, unq, comment hcolor) int {
194-
var mod int
195-
196-
for i := offset; i < len(tokens); i++ {
197-
t := tokens[i]
198-
199-
switch t.Type {
200-
case util.PoSymbols["WS"]:
201-
continue
202-
case util.PoSymbols["Comment"]:
203-
t.Value = comment.Sprint(t.Value)
204-
case util.PoSymbols["String"]:
205-
// NOTE:
206-
// The lexer guarantees that tokens of type "String" are always properly quoted.
207-
// Therefore, it's safe to access the first capture group without additional checks.
208-
unquoted := strRegex.FindStringSubmatch(t.Value)[1]
209-
t.Value = fmt.Sprintf(`"%s"`, unq.Sprint(unquoted))
210-
default:
211-
return mod
212-
}
213-
214-
mod++
215-
tokens[i] = t
216-
}
217-
218-
return mod
219-
}

test.css

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* Default styling rules for PO files when doing terminal output.
2+
Copyright (C) 2006-2007 Free Software Foundation, Inc.
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>. */
16+
17+
.translator-comment {
18+
color: green;
19+
}
20+
21+
.obsolete {
22+
color: green;
23+
}
24+
25+
.extracted-comment {
26+
color: green;
27+
font-weight: bold;
28+
}
29+
30+
.flag {
31+
text-decoration: underline;
32+
}
33+
34+
.fuzzy-flag {
35+
text-decoration: none;
36+
}
37+
38+
.text {
39+
color: magenta;
40+
}
41+
42+
.msgid {
43+
color: red;
44+
}
45+
46+
.msgstr .text {
47+
color: blue;
48+
}
49+
50+
.fuzzy .msgstr .text {
51+
color: red;
52+
}
53+
54+
.format-directive {
55+
font-weight: bold;
56+
}
57+
58+
.invalid-format-directive {
59+
background-color: red;
60+
color: white;
61+
font-weight: bold;
62+
}

test.po

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
msgctxt "h"
2+
msgid "hello worls"
3+
msgid_plural "hi"
4+
msgstr[0] ""
5+
msgstr[1] ""

0 commit comments

Comments
 (0)