Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add templating option #60

Merged
merged 2 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions cmd/translations.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ translations.swift:
static let SOMETHING = NSLocalizedString("SOMETHING", comment: "")
static func SOMETHING_WITH_ARGUMENTS(_ p1: String, _ p2: String) -> String { return NSLocalizedString("SOMETHING_WITH_ARGUMENTS", comment: "").replacingOccurrences(of: "%1", with: p1).replacingOccurrences(of: "%2", with: p2) }
}

You can support your own golang text template to change the output, the above output is generated with the following default template:

// swiftlint:disable all
import Foundation
public struct Translations {
{{- range . }}
{{- if .Arguments }}
static func {{ .Name }}({{ .Arguments }}) -> String { return NSLocalizedString("{{ .Name }}", comment: ""){{ .Replacements }} }
{{- else }}
static let {{ .Name }} = NSLocalizedString("{{ .Name }}", comment: "")
{{- end }}
{{- end }}
}


`
var flags translations.Flags
var configurations []string
Expand All @@ -109,6 +125,7 @@ translations.swift:
cmd.Flags().StringArrayVarP(&configurations, "configuration", "c", make([]string, 0), "A configuration string consisting of space separated row index and output path. Multiple configurations can be added, but one is required")
cmd.Flags().IntVarP(&flags.DefaultValueIndex, "main-index", "m", 0, "Required by type ios and by option fill-in. The index of the main/default language row")
cmd.Flags().StringVarP(&flags.Output, "output", "o", "", "Required for type ios. A path for the generated output")
cmd.Flags().StringVarP(&flags.Template, "template", "p", "", "Only for type ios and optional. A path for the template to generate from")
cmd.Flags().BoolVarP(&flags.FillIn, "fill-in", "l", false, "Fill in the value from the main/default language if a value is missing for the current language")
cmd.MarkFlagRequired("input")
cmd.MarkFlagRequired("kind")
Expand Down
17 changes: 17 additions & 0 deletions docs/generated/lane_translations_generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ translations.swift:
static func SOMETHING_WITH_ARGUMENTS(_ p1: String, _ p2: String) -> String { return NSLocalizedString("SOMETHING_WITH_ARGUMENTS", comment: "").replacingOccurrences(of: "%1", with: p1).replacingOccurrences(of: "%2", with: p2) }
}

You can support your own golang text template to change the output, the above output is generated with the following default template:

// swiftlint:disable all
import Foundation
public struct Translations {
{{- range . }}
{{- if .Arguments }}
static func {{ .Name }}({{ .Arguments }}) -> String { return NSLocalizedString("{{ .Name }}", comment: ""){{ .Replacements }} }
{{- else }}
static let {{ .Name }} = NSLocalizedString("{{ .Name }}", comment: "")
{{- end }}
{{- end }}
}




```
lane translations generate [flags]
Expand All @@ -63,6 +79,7 @@ lane translations generate [flags]
-i, --input string Path to a CSV file containing a key row and a row for each language (Required)
-m, --main-index int Required by type ios and by option fill-in. The index of the main/default language row
-o, --output string Required for type ios. A path for the generated output
-p, --template string Only for type ios and optional. A path for the template to generate from
-t, --type string The type of output to generate, valid options are 'ios', 'android' or 'json' (Required)
```

Expand Down
7 changes: 7 additions & 0 deletions internal/translations/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Flags struct {
DefaultValueIndex int
Output string
FillIn bool
Template string
}

func (f *Flags) validate() error {
Expand Down Expand Up @@ -44,6 +45,12 @@ func (f *Flags) validate() error {
return err
}

if len(f.Template) > 0 {
if _, err := os.Stat(f.Template); err != nil {
return err
}
}

if isIOS {
if len(f.Output) == 0 {
return fmt.Errorf("output not provided")
Expand Down
26 changes: 26 additions & 0 deletions internal/translations/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,32 @@ var validationCases = []struct {
},
true,
},
{
"ios-with-template-valid",
Flags{
Input: "testdata/input.csv",
Kind: "ios",
KeyIndex: 1,
DefaultValueIndex: 1,
Output: "testdata/out.put",
FillIn: true,
Template: "testdata/templated-ios-support/file.tmpl",
},
true,
},
{
"ios-with-template-invalid",
Flags{
Input: "testdata/input.csv",
Kind: "ios",
KeyIndex: 1,
DefaultValueIndex: 1,
Output: "testdata/out.put",
FillIn: true,
Template: "does/not/exist.file",
},
false,
},
}

func TestFlagsValidate(t *testing.T) {
Expand Down
33 changes: 33 additions & 0 deletions internal/translations/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,36 @@ func TestConfigurationCases(t *testing.T) {
})
}
}

func TestTemplatedIos(t *testing.T) {
flags := Flags{
Input: "testdata/templated-ios-support/input.csv",
Kind: "ios",
KeyIndex: 1,
DefaultValueIndex: 2,
Output: "../../build/Translations.swift",
Template: "testdata/templated-ios-support/file.tmpl",
}
configurations := []string{"2 ../../build/en.strings"}

err := Generate(context.Background(), &flags, configurations)

assert.Nil(t, err)
equalFiles(t, "testdata/templated-ios-support/ios-swift.expected", "../../build/Translations.swift")
}

func TestInvalidTemplate(t *testing.T) {
flags := Flags{
Input: "testdata/templated-ios-support/input.csv",
Kind: "ios",
KeyIndex: 1,
DefaultValueIndex: 2,
Output: "../../build/Translations.swift",
Template: "testdata/templated-ios-support/invalid.tmpl",
}
configurations := []string{"2 ../../build/en.strings"}

err := Generate(context.Background(), &flags, configurations)

assert.NotNil(t, err)
}
25 changes: 19 additions & 6 deletions internal/translations/language_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,25 @@ func newLanguageFiles(flags *Flags, configurations []string) ([]languageFileWrit

switch flags.Kind {
case iosKind:
supporter := &iosSupportLanguageFile{file: &languageFile{
path: flags.Output,
keyIndex: flags.KeyIndex,
valueIndex: flags.DefaultValueIndex,
useFallback: false,
}}
var template string

if len(flags.Template) > 0 {
buf, err := os.ReadFile(flags.Template)
if err != nil {
return nil, err
}
template = string(buf)
}

supporter := &iosSupportLanguageFile{
file: &languageFile{
path: flags.Output,
keyIndex: flags.KeyIndex,
valueIndex: flags.DefaultValueIndex,
useFallback: false,
},
template: template,
}
list = append(list, supporter)
}

Expand Down
58 changes: 39 additions & 19 deletions internal/translations/language_file.ios.support.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,56 @@ import (
"io"
"regexp"
"strings"
"text/template"
)

type iosSupportLanguageFile struct {
file *languageFile
file *languageFile
template string
}

func (f *iosSupportLanguageFile) Write(translations *translationData) error {
return f.file.write(f, translations)
}

func (f *iosSupportLanguageFile) write(translation *translation, io io.Writer) error {
regex := regexp.MustCompile(`%([0-9]+)`)
type line struct {
Name string
Arguments string
Replacements string
}

header := `// swiftlint:disable all
const iosSupportTemplate = `// swiftlint:disable all
import Foundation
struct Translations {
`
footer := `}
public struct Translations {
{{- range . }}
{{- if .Arguments }}
static func {{ .Name }}({{ .Arguments }}) -> String { return NSLocalizedString("{{ .Name }}", comment: ""){{ .Replacements }} }
{{- else }}
static let {{ .Name }} = NSLocalizedString("{{ .Name }}", comment: "")
{{- end }}
{{- end }}
}
`

_, err := io.Write([]byte(header))
func (f *iosSupportLanguageFile) write(translation *translation, io io.Writer) error {
regex := regexp.MustCompile(`%([0-9]+)`)

tmpl := f.template
if tmpl == "" {
tmpl = iosSupportTemplate
}

generator, err := template.New("tmpl").Parse(tmpl)
if err != nil {
return err
}

list := make([]*line, 0)
for _, k := range translation.keys {
key := strings.ToUpper(k)
value := translation.get(k)
var item *line

var line string
matches := regex.FindAllStringSubmatch(value, -1)
if len(matches) > 0 {
arguments := make([]string, 0)
Expand All @@ -44,19 +64,19 @@ struct Translations {
arguments = append(arguments, fmt.Sprintf("_ p%s: String", match))
replacements = append(replacements, fmt.Sprintf(".replacingOccurrences(of: \"%%%s\", with: p%s)", match, match))
}
argumentsString := strings.Join(arguments, ", ")
replacementsString := strings.Join(replacements, "")
line = fmt.Sprintf("\tstatic func %s(%s) -> String { return NSLocalizedString(\"%s\", comment: \"\")%s }\n", key, argumentsString, key, replacementsString)
item = &line{
Name: key,
Arguments: strings.Join(arguments, ", "),
Replacements: strings.Join(replacements, ""),
}
} else {
line = fmt.Sprintf("\tstatic let %s = NSLocalizedString(\"%s\", comment: \"\")\n", key, key)
item = &line{
Name: key,
}
}

_, err := io.Write([]byte(line))
if err != nil {
return err
}
list = append(list, item)
}

_, err = io.Write([]byte(footer))
return err
return generator.Execute(io, list)
}
2 changes: 1 addition & 1 deletion internal/translations/testdata/fill-in/ios-swift.expected
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// swiftlint:disable all
import Foundation
struct Translations {
public struct Translations {
static let SOMETHING = NSLocalizedString("SOMETHING", comment: "")
static let SOMETHING_ONLY_IN_EN = NSLocalizedString("SOMETHING_ONLY_IN_EN", comment: "")
}
2 changes: 1 addition & 1 deletion internal/translations/testdata/ios-swift-empty.expected
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swiftlint:disable all
import Foundation
struct Translations {
public struct Translations {
}
2 changes: 1 addition & 1 deletion internal/translations/testdata/ios-swift.expected
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// swiftlint:disable all
import Foundation
struct Translations {
public struct Translations {
static let SOMETHING = NSLocalizedString("SOMETHING", comment: "")
static let SOMETHING_ESCAPED = NSLocalizedString("SOMETHING_ESCAPED", comment: "")
static let SOMETHING_FOR_XML = NSLocalizedString("SOMETHING_FOR_XML", comment: "")
Expand Down
15 changes: 15 additions & 0 deletions internal/translations/testdata/templated-ios-support/file.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// swiftlint:disable all
import Foundation
struct Texts {
static var bundle = Bundle.main
{{- range . }}
{{- if .Arguments }}
static func {{ .Name }}({{ .Arguments }}) -> String {
return NSLocalizedString("{{ .Name }}", bundle: bundle, comment: "{{ .Name }}")
{{ .Replacements }}
}
{{- else }}
static let {{ .Name }} = NSLocalizedString("{{ .Name }}", bundle: bundle, comment: "{{ .Name }}")
{{- end }}
{{- end }}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
KEY,EN,DA
SOMETHING,Something,Noget
SOMETHING_WITH_ARGUMENTS,Something with %1 and %2,Noget med %1 og %2
16 changes: 16 additions & 0 deletions internal/translations/testdata/templated-ios-support/invalid.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{ define content }}
// swiftlint:disable all
import Foundation
struct Texts {
static var bundle = Bundle.main
{{- range . }}
{{- if .Arguments }}
static func {{ .Name }}({{ .Arguments }}) -> String {
return NSLocalizedString("{{ .Name }}", bundle: bundle, comment: "{{ .Name }}")
{{ .Replacements }}
}
{{- else }}
static let {{ .Name }} = NSLocalizedString("{{ .Name }}", bundle: bundle, comment: "{{ .Name }}")
{{- end }}
{{- end }}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// swiftlint:disable all
import Foundation
struct Texts {
static var bundle = Bundle.main
static let SOMETHING = NSLocalizedString("SOMETHING", bundle: bundle, comment: "SOMETHING")
static func SOMETHING_WITH_ARGUMENTS(_ p1: String, _ p2: String) -> String {
return NSLocalizedString("SOMETHING_WITH_ARGUMENTS", bundle: bundle, comment: "SOMETHING_WITH_ARGUMENTS")
.replacingOccurrences(of: "%1", with: p1).replacingOccurrences(of: "%2", with: p2)
}
}