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 ability to localize all strings output to console #4464

Merged
merged 32 commits into from
Jun 21, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0b79a57
Initial attempt at localization with gotext
sharifelgamal May 13, 2019
e5ac5d8
Merge branch 'master' of github.com:kubernetes/minikube into localiza…
sharifelgamal May 13, 2019
fd75433
fixing go mods
sharifelgamal May 14, 2019
03ebb0e
Merge branch 'master' of github.com:kubernetes/minikube into localiza…
sharifelgamal May 16, 2019
e174f3b
Add semi-functional extractor
sharifelgamal May 17, 2019
fbc11c3
Recursive search for extractor
sharifelgamal May 29, 2019
483f9fb
Merge branch 'master' of github.com:kubernetes/minikube into localiza…
sharifelgamal May 30, 2019
610e4f1
More extractor work
sharifelgamal Jun 6, 2019
3447499
Robust extraction yaaaaaaaay
sharifelgamal Jun 10, 2019
f452f0c
Merge branch 'master' of github.com:kubernetes/minikube into localiza…
sharifelgamal Jun 10, 2019
4630ccc
linting
sharifelgamal Jun 10, 2019
0c43731
Small fixes to make things work
sharifelgamal Jun 10, 2019
aabce58
Refactor extract to not use global variables
sharifelgamal Jun 11, 2019
aa5c2ec
adding back extract
sharifelgamal Jun 11, 2019
3ce070c
extract is now a command
sharifelgamal Jun 13, 2019
58dd381
Merge branch 'master' of github.com:kubernetes/minikube into localiza…
sharifelgamal Jun 13, 2019
b573cb8
adding extract cmd file
sharifelgamal Jun 13, 2019
41f258c
Merge branch 'localization-poc' of github.com:sharifelgamal/minikube …
sharifelgamal Jun 13, 2019
dae9425
small changes
sharifelgamal Jun 14, 2019
5ef94b4
Adding comments for clarity
sharifelgamal Jun 14, 2019
b499226
shorten name of translate function
sharifelgamal Jun 17, 2019
6cd86b7
don't make extract an end-user command
sharifelgamal Jun 17, 2019
8da5f2b
General cleanup for clarity
sharifelgamal Jun 17, 2019
8308aa5
adding test
sharifelgamal Jun 18, 2019
97e2cd1
Simplifying and correcting the extract code. Test passes now.
sharifelgamal Jun 20, 2019
062a453
test actually passes now
sharifelgamal Jun 20, 2019
d1eea53
add demo translation file for zh-CN
sharifelgamal Jun 20, 2019
e8329f6
Merge branch 'master' of github.com:kubernetes/minikube into localiza…
sharifelgamal Jun 20, 2019
7fc7b24
fix linting errors
sharifelgamal Jun 20, 2019
3be580c
add comment for top level command and add chinese transkations
sharifelgamal Jun 20, 2019
7a8fb7d
add more entries to the blacklist and clean up translation files
sharifelgamal Jun 20, 2019
8f5026d
small fix
sharifelgamal Jun 20, 2019
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
39 changes: 26 additions & 13 deletions pkg/minikube/extract/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ import (
// A list of strings to explicitly omit from translation files.
var blacklist = []string{"%s: %v"}
sharifelgamal marked this conversation as resolved.
Show resolved Hide resolved

// currentState is a struct that represent the current state of the extraction process
type currentState struct {
// state is a struct that represent the current state of the extraction process
type state struct {
// The list of functions to check for
funcs map[string]struct{}

Expand All @@ -60,7 +60,7 @@ type currentState struct {
}

// newExtractor initializes state for extraction
func newExtractor(functionsToCheck []string) *currentState {
func newExtractor(functionsToCheck []string) *state {
funcs := make(map[string]struct{})
fs := stack.New()

Expand All @@ -69,7 +69,7 @@ func newExtractor(functionsToCheck []string) *currentState {
fs.Push(f)
}

return &currentState{
return &state{
funcs: funcs,
fs: fs,
translations: make(map[string]interface{}),
Expand All @@ -78,6 +78,16 @@ func newExtractor(functionsToCheck []string) *currentState {

// TranslatableStrings finds all strings to that need to be translated in paths and prints them out to all json files in output
func TranslatableStrings(paths []string, functions []string, output string) {
cwd, err := os.Getwd()
if err != nil {
exit.WithError("Getting current working directory", err)
sharifelgamal marked this conversation as resolved.
Show resolved Hide resolved
}

if strings.Contains(cwd, "cmd") {
fmt.Println("Run extract.go from the minikube root directory.")
os.Exit(1)
}

e := newExtractor(functions)

fmt.Println("Compiling translation strings...")
Expand All @@ -100,7 +110,7 @@ func TranslatableStrings(paths []string, functions []string, output string) {
}
}

err := writeStringsToFiles(e, output)
err = writeStringsToFiles(e, output)

if err != nil {
exit.WithError("Writing translation files", err)
Expand All @@ -114,7 +124,7 @@ func shouldCheckFile(path string) bool {
}

// inspectFile goes through the given file line by line looking for translatable strings
func inspectFile(e *currentState) error {
func inspectFile(e *state) error {
fset := token.NewFileSet()
r, err := ioutil.ReadFile(e.filename)
if err != nil {
Expand Down Expand Up @@ -163,7 +173,7 @@ func inspectFile(e *currentState) error {
}

// checkStmt checks each line to see if it's a call to print a string out to the console
func checkStmt(stmt ast.Stmt, e *currentState) {
func checkStmt(stmt ast.Stmt, e *state) {
//fmt.Printf("%s: %s\n", stmt, reflect.TypeOf(stmt))

// If this line is an expression, see if it's a function call
Expand All @@ -185,7 +195,7 @@ func checkStmt(stmt ast.Stmt, e *currentState) {
}

// checkIfStmt does if-statement-specific checks, especially relating to else stmts
func checkIfStmt(stmt *ast.IfStmt, e *currentState) {
func checkIfStmt(stmt *ast.IfStmt, e *state) {
for _, s := range stmt.Body.List {
checkStmt(s, e)
}
Expand All @@ -206,7 +216,7 @@ func checkIfStmt(stmt *ast.IfStmt, e *currentState) {
}

// checkCallExpression takes a function call, and checks its arguments for strings
func checkCallExpression(expr *ast.ExprStmt, e *currentState) {
func checkCallExpression(expr *ast.ExprStmt, e *state) {
s, ok := expr.X.(*ast.CallExpr)

// This line isn't a function call
Expand Down Expand Up @@ -253,7 +263,7 @@ func checkCallExpression(expr *ast.ExprStmt, e *currentState) {
}

// checkIdentForStringValye takes a identifier and sees if it's a variable assigned to a string
func checkIdentForStringValue(i *ast.Ident, e *currentState) bool {
func checkIdentForStringValue(i *ast.Ident, e *state) bool {
// This identifier is nil
if i.Obj == nil {
return false
Expand Down Expand Up @@ -281,7 +291,7 @@ func checkIdentForStringValue(i *ast.Ident, e *currentState) bool {
}

// addStringToList takes a string, makes sure it's meant to be translated then adds it to the list if so
func addStringToList(s string, e *currentState) bool {
func addStringToList(s string, e *state) bool {
// Empty strings don't need translating
if len(s) <= 2 {
return false
Expand Down Expand Up @@ -319,14 +329,17 @@ func addStringToList(s string, e *currentState) bool {
}

// writeStringsToFiles writes translations to all translation files in output
func writeStringsToFiles(e *currentState, output string) error {
func writeStringsToFiles(e *state, output string) error {
err := filepath.Walk(output, func(path string, info os.FileInfo, err error) error {
if err != nil {
return errors.Wrap(err, "accessing path")
}
if info.Mode().IsDir() {
return nil
}
if !strings.HasSuffix(path, ".json") {
return nil
}
fmt.Printf("Writing to %s\n", filepath.Base(path))
var currentTranslations map[string]interface{}
f, err := ioutil.ReadFile(path)
Expand Down Expand Up @@ -361,7 +374,7 @@ func writeStringsToFiles(e *currentState, output string) error {
}

// addParentFuncToList adds the current parent function to the list of functions to inspect more closely.
func addParentFuncToList(e *currentState) {
func addParentFuncToList(e *state) {
if _, ok := e.funcs[e.parentFunc]; !ok {
e.funcs[e.parentFunc] = struct{}{}
e.fs.Push(e.parentFunc)
Expand Down
42 changes: 42 additions & 0 deletions pkg/minikube/extract/extract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package extract

import (
"encoding/json"
"io/ioutil"
"reflect"
"testing"
)

func TestExtract(t *testing.T) {
// The file to scan
paths := []string{"testdata/sample_file.go"}

// The function we care about
functions := []string{"PrintToScreen"}

// The directory where the sample translation file is in
output := "testdata/"

expected := map[string]interface{}{
"Hint: This is not a URL, come on.": "",
"Holy cow I'm in a loop!": "Something else",
"This is a variable with a string assigned": "",
"This was a choice: %s": "Something",
"Wow another string: %s": "",
}

TranslatableStrings(paths, functions, output)

var got map[string]interface{}
f, err := ioutil.ReadFile("testdata/en-US.json")
if err != nil {
t.Fatalf("Reading json file: %s", err)
}

json.Unmarshal(f, &got)

if !reflect.DeepEqual(expected, got) {
t.Fatalf("Translation JSON not equal: expected %v, got %v", expected, got)
}

}
4 changes: 4 additions & 0 deletions pkg/minikube/extract/testdata/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"Holy cow I'm in a loop!": "Something else",
"This was a choice: %s": "Something"
}
48 changes: 48 additions & 0 deletions pkg/minikube/extract/testdata/sample_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package extract

import "fmt"

func DoSomeStuff() {
// Test with a URL
PrintToScreenNoInterface("http://kubernetes.io")

// Test with something that Go thinks looks like a URL
PrintToScreenNoInterface("Hint: This is not a URL, come on.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha love this


// Try with an integer
PrintToScreenNoInterface("5")

// Try with a sudo command
path := "."
PrintToScreen("sudo ls %s", path)

DoSomeOtherStuff(true, 4, "I think this should work")

v := "This is a variable with a string assigned"
PrintToScreenNoInterface(v)
}

func DoSomeOtherStuff(choice bool, i int, s string) {

// Let's try an if statement
if choice {
PrintToScreen("This was a choice: %s", s)
} else if i > 5 {
PrintToScreen("Wow another string: %s", i)
} else {
for i > 10 {
PrintToScreenNoInterface("Holy cow I'm in a loop!")
i = i + 1
}
}

}

func PrintToScreenNoInterface(s string) {
PrintToScreen(s, nil)
}

// This will be the function we'll focus the extractor on
func PrintToScreen(s string, i interface{}) {
fmt.Printf(s, i)
}