Skip to content

Commit

Permalink
feat: add i18n for frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
HeartLinked committed May 29, 2024
1 parent d5735db commit 2470bac
Show file tree
Hide file tree
Showing 8 changed files with 508 additions and 239 deletions.
146 changes: 146 additions & 0 deletions i18n/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package i18n

import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/casbin/caswaf/util"
)

type I18nData map[string]map[string]string

var (
reI18nFrontend *regexp.Regexp
reI18nBackendObject *regexp.Regexp
reI18nBackendController *regexp.Regexp
)

func init() {
reI18nFrontend, _ = regexp.Compile("i18next.t\\(\"(.*?)\"\\)")
reI18nBackendObject, _ = regexp.Compile("i18n.Translate\\((.*?)\"\\)")
reI18nBackendController, _ = regexp.Compile("c.T\\((.*?)\"\\)")
}

func getAllI18nStringsFrontend(fileContent string) []string {
res := []string{}

matches := reI18nFrontend.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}

for _, match := range matches {
res = append(res, match[1])
}
return res
}

func getAllI18nStringsBackend(fileContent string, isObjectPackage bool) []string {
res := []string{}
if isObjectPackage {
matches := reI18nBackendObject.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
match := strings.SplitN(match[1], ",", 2)
res = append(res, match[1][2:])
}
} else {
matches := reI18nBackendController.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
res = append(res, match[1][1:])
}
}

return res
}

func getAllFilePathsInFolder(folder string, fileSuffix string) []string {
res := []string{}
err := filepath.Walk(folder,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if !strings.HasSuffix(info.Name(), fileSuffix) {
return nil
}

res = append(res, path)
fmt.Println(path, info.Name())
return nil
})
if err != nil {
panic(err)
}

return res
}

func parseAllWords(category string) *I18nData {
var paths []string
if category == "backend" {
paths = getAllFilePathsInFolder("../", ".go")
} else {
paths = getAllFilePathsInFolder("../web/src", ".js")
}

allWords := []string{}
for _, path := range paths {
fileContent := util.ReadStringFromPath(path)

var words []string
if category == "backend" {
isObjectPackage := strings.Contains(path, "object")
words = getAllI18nStringsBackend(fileContent, isObjectPackage)
} else {
words = getAllI18nStringsFrontend(fileContent)
}
allWords = append(allWords, words...)
}
fmt.Printf("%v\n", allWords)

data := I18nData{}
for _, word := range allWords {
tokens := strings.SplitN(word, ":", 2)
namespace := tokens[0]
key := tokens[1]

if _, ok := data[namespace]; !ok {
data[namespace] = map[string]string{}
}
data[namespace][key] = key
}

return &data
}

func applyToOtherLanguage(category string, language string, newData *I18nData) {
oldData := readI18nFile(category, language)
println(oldData)

applyData(newData, oldData)
writeI18nFile(category, language, newData)
}
76 changes: 76 additions & 0 deletions i18n/generate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build !skipCi
// +build !skipCi

package i18n

import "testing"

func TestGenerateI18nFrontend(t *testing.T) {
data := parseAllWords("frontend")

applyToOtherLanguage("frontend", "en", data)
applyToOtherLanguage("frontend", "zh", data)
//applyToOtherLanguage("frontend", "es", data)
//applyToOtherLanguage("frontend", "fr", data)
//applyToOtherLanguage("frontend", "de", data)
//applyToOtherLanguage("frontend", "id", data)
//applyToOtherLanguage("frontend", "ja", data)
//applyToOtherLanguage("frontend", "ko", data)
//applyToOtherLanguage("frontend", "ru", data)
//applyToOtherLanguage("frontend", "vi", data)
//applyToOtherLanguage("frontend", "pt", data)
//applyToOtherLanguage("frontend", "it", data)
//applyToOtherLanguage("frontend", "ms", data)
//applyToOtherLanguage("frontend", "tr", data)
//applyToOtherLanguage("frontend", "ar", data)
//applyToOtherLanguage("frontend", "he", data)
//applyToOtherLanguage("frontend", "nl", data)
//applyToOtherLanguage("frontend", "pl", data)
//applyToOtherLanguage("frontend", "fi", data)
//applyToOtherLanguage("frontend", "sv", data)
//applyToOtherLanguage("frontend", "uk", data)
//applyToOtherLanguage("frontend", "kk", data)
//applyToOtherLanguage("frontend", "fa", data)
}

func TestGenerateI18nBackend(t *testing.T) {
data := parseAllWords("backend")

applyToOtherLanguage("backend", "en", data)
applyToOtherLanguage("backend", "zh", data)
//applyToOtherLanguage("backend", "es", data)
//applyToOtherLanguage("backend", "fr", data)
//applyToOtherLanguage("backend", "de", data)
//applyToOtherLanguage("backend", "id", data)
//applyToOtherLanguage("backend", "ja", data)
//applyToOtherLanguage("backend", "ko", data)
//applyToOtherLanguage("backend", "ru", data)
//applyToOtherLanguage("backend", "vi", data)
//applyToOtherLanguage("backend", "pt", data)
//applyToOtherLanguage("backend", "it", data)
//applyToOtherLanguage("backend", "ms", data)
//applyToOtherLanguage("backend", "tr", data)
//applyToOtherLanguage("backend", "ar", data)
//applyToOtherLanguage("backend", "he", data)
//applyToOtherLanguage("backend", "nl", data)
//applyToOtherLanguage("backend", "pl", data)
//applyToOtherLanguage("backend", "fi", data)
//applyToOtherLanguage("backend", "sv", data)
//applyToOtherLanguage("backend", "uk", data)
//applyToOtherLanguage("backend", "kk", data)
//applyToOtherLanguage("backend", "fa", data)
}
1 change: 1 addition & 0 deletions i18n/locales/en/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions i18n/locales/zh/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
101 changes: 101 additions & 0 deletions i18n/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package i18n

import (
"embed"
"fmt"
"strings"

"github.com/casbin/caswaf/util"
)

//go:embed locales/*/data.json
var f embed.FS

var langMap = make(map[string]map[string]map[string]string) // for example : langMap[en][account][Invalid information] = Invalid information

func getI18nFilePath(category string, language string) string {
if category == "backend" {
return fmt.Sprintf("../i18n/locales/%s/data.json", language)
} else {
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
}
}

func readI18nFile(category string, language string) *I18nData {
s := util.ReadStringFromPath(getI18nFilePath(category, language))

data := &I18nData{}
err := util.JsonToStruct(s, data)
if err != nil {
panic(err)
}
return data
}

func writeI18nFile(category string, language string, data *I18nData) {
s := util.StructToJson(data)
s = strings.ReplaceAll(s, "\\u0026", "&")
s += "\n"
println(s)

util.WriteStringToPath(s, getI18nFilePath(category, language))
}

func applyData(data1 *I18nData, data2 *I18nData) {
for namespace, pairs2 := range *data2 {
if _, ok := (*data1)[namespace]; !ok {
continue
}

pairs1 := (*data1)[namespace]

for key, value := range pairs2 {
if _, ok := pairs1[key]; !ok {
continue
}

pairs1[key] = value
}
}
}

func Translate(language string, errorText string) string {
tokens := strings.SplitN(errorText, ":", 2)
if !strings.Contains(errorText, ":") || len(tokens) != 2 {
return fmt.Sprintf("Translate error: the error text doesn't contain \":\", errorText = %s", errorText)
}

if langMap[language] == nil {
file, err := f.ReadFile(fmt.Sprintf("locales/%s/data.json", language))
if err != nil {
return fmt.Sprintf("Translate error: the language \"%s\" is not supported, err = %s", language, err.Error())
}

data := I18nData{}
err = util.JsonToStruct(string(file), &data)
if err != nil {
panic(err)
}
langMap[language] = data
}

res := langMap[language][tokens[0]][tokens[1]]
if res == "" {
res = tokens[1]
}
return res
}
2 changes: 1 addition & 1 deletion web/src/SiteListPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class SiteListPage extends BaseListPage {
},
},
{
title: i18next.t("site: Enable WAF"),
title: i18next.t("site:Enable WAF"),
dataIndex: "enableWaf",
key: "enableWaf",
width: "120px",
Expand Down
Loading

0 comments on commit 2470bac

Please sign in to comment.