Skip to content

Commit d53a15b

Browse files
authored
Add support for i18n in the cli (#676)
* add po message catalog implementation * add po parser for i18n cmd * add po merge function * add command to generate en po file from source * add command to update local po files with en catalog change * add task to generate po files * add locale option for i18n * add dependencies for i18n * add unit test for i18n * add godoc to exported fields * add rice box for i18n messages * add readme for i18n module * update README to add instruction to install go-rice * remove warning log in case locale is not found * add command to pull and push translation files from/to transifex * remove unused import * dont generate new rice file if there are no translation changes * add copyright headers * use 'tr' function call as indicator for translations * adding documentation to pull,push translations and adding new languages * push only the reference translation catalog * add check on PR for updated catalog not commited * push message catalog to transifex * pull translations fro transifex weekly * get locale identifier from diferent OSes * get locale identifier from diferent OSes * match locale algo * add locale match test * preserve LF in translation string unchanged * init config before executing command * create arduino cmd dynamically * make all command init dynamically * save all message occurrences in catalog * add translatable cli usage template * add messages for cli usage template * remove standalone i18n message check * add more i18n tests * fix po parsing correctness and implement tests * fix configuration path search tests * update catalog command to find go files * update catalog with new path * fix docsgen command * remove dependency on shell script for windows compat * fix test workflow * update setup-go to v2 * fail i18n:check task if catalog was not updated * replace windows separator with forward slash * use filepath function to translate windows paths * only update en catalog
1 parent 4b9c924 commit d53a15b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1800
-85
lines changed

Diff for: .github/workflows/i18n-nightly-push.yaml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: i18n-nightly-push
2+
3+
on:
4+
schedule:
5+
# run every day at 1AM
6+
- cron: '0 1 * * *'
7+
8+
jobs:
9+
push-to-transifex:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@master
14+
15+
- name: Install Go
16+
uses: actions/setup-go@v2
17+
with:
18+
go-version: '1.14'
19+
20+
- name: Install Taskfile
21+
uses: Arduino/actions/setup-taskfile@master
22+
with:
23+
repo-token: ${{ secrets.GITHUB_TOKEN }}
24+
25+
- name: Run task i18n:push
26+
run: task i18n:push
27+
env:
28+
TRANSIFEX_PROJECT: ${{ secrets.TRANSIFEX_PROJECT }}
29+
TRANSIFEX_RESOURCE: ${{ secrets.TRANSIFEX_RESOURCE }}
30+
TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }}

Diff for: .github/workflows/i18n-weekly-pull.yaml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: i18n-weekly-pull
2+
3+
on:
4+
schedule:
5+
# run every monday at 2AM
6+
- cron: '0 2 * * 1'
7+
8+
jobs:
9+
pull-from-transifex:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@master
14+
15+
- name: Install Go
16+
uses: actions/setup-go@v2
17+
with:
18+
go-version: '1.14'
19+
20+
- name: Install Go deps
21+
run: |
22+
go get github.com/GeertJohan/go.rice/rice
23+
24+
- name: Install Taskfile
25+
uses: Arduino/actions/setup-taskfile@master
26+
with:
27+
repo-token: ${{ secrets.GITHUB_TOKEN }}
28+
29+
- name: Run task i18n:pull
30+
run: task i18n:pull
31+
env:
32+
TRANSIFEX_PROJECT: ${{ secrets.TRANSIFEX_PROJECT }}
33+
TRANSIFEX_RESOURCE: ${{ secrets.TRANSIFEX_RESOURCE }}
34+
TRANSIFEX_API_KEY: ${{ secrets.TRANSIFEX_API_KEY }}
35+
36+
- name: Create Pull Request
37+
uses: peter-evans/create-pull-request@v2
38+
with:
39+
commit-message: Updated translation files
40+
title: Updated translation files
41+
branch: i18n/translations-update

Diff for: .github/workflows/test.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
uses: actions/checkout@master
2424

2525
- name: Install Go
26-
uses: actions/setup-go@v1
26+
uses: actions/setup-go@v2
2727
with:
2828
go-version: '1.14'
2929

@@ -35,6 +35,7 @@ jobs:
3535
go get github.com/golangci/govet
3636
go get golang.org/x/lint/golint
3737
go get github.com/golang/protobuf/protoc-gen-go
38+
go get github.com/GeertJohan/go.rice/rice
3839
shell: bash
3940

4041
- name: Install Taskfile

Diff for: Taskfile.yml

+32
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ tasks:
102102
- test -z $(go fmt {{ default .DEFAULT_TARGETS .TARGETS }})
103103
- go vet {{ default .DEFAULT_TARGETS .TARGETS }}
104104
- "'{{.GOLINTBIN}}' {{.GOLINTFLAGS}} {{ default .DEFAULT_TARGETS .TARGETS }}"
105+
- task: i18n:check
105106

106107
check-legacy:
107108
desc: Check fmt and lint for the `legacy` package
@@ -114,6 +115,36 @@ tasks:
114115
cmds:
115116
- go test -run TestWithClientE2E ./commands/daemon
116117

118+
i18n:update:
119+
desc: Updates i18n files
120+
cmds:
121+
- go run ./i18n/cmd/main.go catalog generate . > ./i18n/data/en.po
122+
- task: i18n:generate
123+
124+
i18n:pull:
125+
desc: Pull i18n files from transifex
126+
cmds:
127+
- go run ./i18n/cmd/main.go transifex pull -l {{.I18N_LANGS}} ./i18n/data
128+
- task: i18n:generate
129+
130+
i18n:push:
131+
desc: Push i18n files to transifex
132+
cmds:
133+
- go run ./i18n/cmd/main.go transifex push ./i18n/data
134+
135+
i18n:check:
136+
desc: Check if the i18n message catalog was updated
137+
cmds:
138+
- task: i18n:update
139+
- git add -N ./i18n/data
140+
- git diff --exit-code ./i18n/data
141+
142+
i18n:generate:
143+
desc: Generate embedded i18n catalog files
144+
cmds:
145+
- git add -N ./i18n/data
146+
- git diff --exit-code ./i18n/data &> /dev/null || (cd ./i18n && rice embed-go)
147+
117148
vars:
118149
# all modules of this project except for "legacy/..." module
119150
DEFAULT_TARGETS:
@@ -138,3 +169,4 @@ vars:
138169
DOCS_VERSION: dev
139170
DOCS_ALIAS: ""
140171
DOCS_REMOTE: "origin"
172+
I18N_LANGS: "pt_BR"

Diff for: cli/board/board.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func NewCommand() *cobra.Command {
3636
boardCommand.AddCommand(initAttachCommand())
3737
boardCommand.AddCommand(initDetailsCommand())
3838
boardCommand.AddCommand(initListCommand())
39-
boardCommand.AddCommand(listAllCommand)
39+
boardCommand.AddCommand(initListAllCommand())
4040

4141
return boardCommand
4242
}

Diff for: cli/board/details.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package board
1818
import (
1919
"context"
2020
"fmt"
21+
"os"
22+
2123
"github.com/arduino/arduino-cli/cli/errorcodes"
2224
"github.com/arduino/arduino-cli/cli/feedback"
2325
"github.com/arduino/arduino-cli/cli/instance"
@@ -26,7 +28,6 @@ import (
2628
"github.com/arduino/arduino-cli/table"
2729
"github.com/fatih/color"
2830
"github.com/spf13/cobra"
29-
"os"
3031
)
3132

3233
var showFullDetails bool

Diff for: cli/board/listall.go

+14-11
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,20 @@ import (
2929
"github.com/spf13/cobra"
3030
)
3131

32-
var listAllCommand = &cobra.Command{
33-
Use: "listall [boardname]",
34-
Short: "List all known boards and their corresponding FQBN.",
35-
Long: "" +
36-
"List all boards that have the support platform installed. You can search\n" +
37-
"for a specific board if you specify the board name",
38-
Example: "" +
39-
" " + os.Args[0] + " board listall\n" +
40-
" " + os.Args[0] + " board listall zero",
41-
Args: cobra.ArbitraryArgs,
42-
Run: runListAllCommand,
32+
func initListAllCommand() *cobra.Command {
33+
var listAllCommand = &cobra.Command{
34+
Use: "listall [boardname]",
35+
Short: "List all known boards and their corresponding FQBN.",
36+
Long: "" +
37+
"List all boards that have the support platform installed. You can search\n" +
38+
"for a specific board if you specify the board name",
39+
Example: "" +
40+
" " + os.Args[0] + " board listall\n" +
41+
" " + os.Args[0] + " board listall zero",
42+
Args: cobra.ArbitraryArgs,
43+
Run: runListAllCommand,
44+
}
45+
return listAllCommand
4346
}
4447

4548
// runListAllCommand list all installed boards

Diff for: cli/cli.go

+15-55
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"fmt"
2020
"io/ioutil"
2121
"os"
22-
"path/filepath"
2322
"strings"
2423

2524
"github.com/arduino/arduino-cli/cli/board"
@@ -38,7 +37,7 @@ import (
3837
"github.com/arduino/arduino-cli/cli/sketch"
3938
"github.com/arduino/arduino-cli/cli/upload"
4039
"github.com/arduino/arduino-cli/cli/version"
41-
"github.com/arduino/arduino-cli/configuration"
40+
"github.com/arduino/arduino-cli/i18n"
4241
"github.com/arduino/arduino-cli/inventory"
4342
"github.com/mattn/go-colorable"
4443
"github.com/rifflock/lfshook"
@@ -48,23 +47,29 @@ import (
4847
)
4948

5049
var (
50+
verbose bool
51+
outputFormat string
52+
configFile string
53+
)
54+
55+
// NewCommand creates a new ArduinoCli command root
56+
func NewCommand() *cobra.Command {
57+
cobra.AddTemplateFunc("tr", i18n.Tr)
58+
5159
// ArduinoCli is the root command
52-
ArduinoCli = &cobra.Command{
60+
arduinoCli := &cobra.Command{
5361
Use: "arduino-cli",
5462
Short: "Arduino CLI.",
5563
Long: "Arduino Command Line Interface (arduino-cli).",
5664
Example: " " + os.Args[0] + " <command> [flags...]",
5765
PersistentPreRun: preRun,
5866
}
5967

60-
verbose bool
61-
outputFormat string
62-
configFile string
63-
)
68+
arduinoCli.SetUsageTemplate(usageTemplate)
6469

65-
// Init the cobra root command
66-
func init() {
67-
createCliCommandTree(ArduinoCli)
70+
createCliCommandTree(arduinoCli)
71+
72+
return arduinoCli
6873
}
6974

7075
// this is here only for testing
@@ -120,52 +125,7 @@ func parseFormatString(arg string) (feedback.OutputFormat, bool) {
120125
return f, found
121126
}
122127

123-
// This function is here to replicate the old logic looking for a config
124-
// file in the parent tree of the CWD, aka "project config".
125-
// Please
126-
func searchConfigTree(cwd string) string {
127-
// go back up to root and search for the config file
128-
for {
129-
if _, err := os.Stat(filepath.Join(cwd, "arduino-cli.yaml")); err == nil {
130-
// config file found
131-
return cwd
132-
} else if os.IsNotExist(err) {
133-
// no config file found
134-
next := filepath.Dir(cwd)
135-
if next == cwd {
136-
return ""
137-
}
138-
cwd = next
139-
} else {
140-
// some error we can't handle happened
141-
return ""
142-
}
143-
}
144-
}
145-
146128
func preRun(cmd *cobra.Command, args []string) {
147-
//
148-
// Prepare the configuration system
149-
//
150-
configPath := ""
151-
152-
// get cwd, if something is wrong don't do anything and let
153-
// configuration init proceed
154-
if cwd, err := os.Getwd(); err == nil {
155-
configPath = searchConfigTree(cwd)
156-
}
157-
158-
// override the config path if --config-file was passed
159-
if fi, err := os.Stat(configFile); err == nil {
160-
if fi.IsDir() {
161-
configPath = configFile
162-
} else {
163-
configPath = filepath.Dir(configFile)
164-
}
165-
}
166-
167-
// initialize the config system
168-
configuration.Init(configPath)
169129
configFile := viper.ConfigFileUsed()
170130

171131
// initialize inventory

Diff for: cli/config/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func NewCommand() *cobra.Command {
2929
Example: " " + os.Args[0] + " config init",
3030
}
3131

32-
configCommand.AddCommand(dumpCmd)
32+
configCommand.AddCommand(initDumpCmd())
3333
configCommand.AddCommand(initInitCommand())
3434

3535
return configCommand

Diff for: cli/config/dump.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@ import (
2525
"gopkg.in/yaml.v2"
2626
)
2727

28-
var dumpCmd = &cobra.Command{
29-
Use: "dump",
30-
Short: "Prints the current configuration",
31-
Long: "Prints the current configuration.",
32-
Example: " " + os.Args[0] + " config dump",
33-
Args: cobra.NoArgs,
34-
Run: runDumpCommand,
28+
func initDumpCmd() *cobra.Command {
29+
var dumpCmd = &cobra.Command{
30+
Use: "dump",
31+
Short: "Prints the current configuration",
32+
Long: "Prints the current configuration.",
33+
Example: " " + os.Args[0] + " config dump",
34+
Args: cobra.NoArgs,
35+
Run: runDumpCommand,
36+
}
37+
return dumpCmd
3538
}
3639

3740
// output from this command requires special formatting, let's create a dedicated

Diff for: cli/usage.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package cli
17+
18+
import (
19+
"github.com/arduino/arduino-cli/i18n"
20+
)
21+
22+
// Declare ids used in usage
23+
var (
24+
tr = i18n.Tr
25+
_ = tr("Usage:")
26+
_ = tr("Aliases:")
27+
_ = tr("Examples:")
28+
_ = tr("Available Commands:")
29+
_ = tr("Flags:")
30+
_ = tr("Global Flags:")
31+
_ = tr("Additional help topics:")
32+
_ = tr("Use %s for more information about a command.")
33+
)
34+
35+
const usageTemplate = `{{tr "Usage:"}}{{if .Runnable}}
36+
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
37+
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
38+
39+
{{tr "Aliases:"}}
40+
{{.NameAndAliases}}{{end}}{{if .HasExample}}
41+
42+
{{tr "Examples:"}}
43+
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
44+
45+
{{tr "Available Commands:"}}{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
46+
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
47+
48+
{{tr "Flags:"}}
49+
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
50+
51+
{{tr "Global Flags:"}}
52+
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
53+
54+
{{tr "Additional help topics:"}}{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
55+
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
56+
57+
{{tr "Use %s for more information about a command." (printf "%s %s" .CommandPath "[command] --help" | printf "%q")}}{{end}}
58+
`

0 commit comments

Comments
 (0)