github.com/erda-project/erda-infra@v1.0.9/providers/component-protocol/protocol/translator/translator.go (about) 1 // Copyright (c) 2021 Terminus, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // TODO switch to servicehub listener mechanism later, now this mechanism is missing. 16 17 package translator 18 19 import ( 20 "bytes" 21 _ "embed" // embed 22 "fmt" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "regexp" 27 "strings" 28 29 "github.com/recallsong/go-utils/reflectx" 30 "github.com/sirupsen/logrus" 31 32 cfg "github.com/erda-project/erda-infra/pkg/config" 33 "github.com/erda-project/erda-infra/providers/i18n" 34 ) 35 36 // InternalI18nConfigs contains all protocl internal i18n configs. 37 // 38 //go:embed i18n-cp-internal.yaml 39 var InternalI18nConfigs string 40 41 // Tran is a translator. 42 type Tran struct { 43 dic map[string]map[string]string 44 } 45 46 // NewInternalTranslator . 47 func NewInternalTranslator() *Tran { 48 // make embed content as a temp file 49 f, _ := ioutil.TempFile(os.TempDir(), "*.yaml") 50 defer func() { 51 if err := f.Close(); err != nil { 52 logrus.Errorf("failed to close i18n config file, err: %v", err) 53 } 54 }() 55 if _, err := f.WriteString(InternalI18nConfigs); err != nil { 56 panic(fmt.Errorf("failed to write i18n config content to temp file, err: %v", err)) 57 } 58 fPath, err := filepath.Abs(f.Name()) 59 if err != nil { 60 panic(fmt.Errorf("failed to get abs path of temp i18n config file, err: %v", err)) 61 } 62 defer func() { 63 if err := os.RemoveAll(fPath); err != nil { 64 logrus.Errorf("failed to remove temp i18n config file, err: %v", err) 65 } 66 }() 67 68 // load to dic 69 dic := make(map[string]map[string]string) 70 if err := loadToDic(fPath, dic); err != nil { 71 panic(err) 72 } 73 return &Tran{dic: dic} 74 } 75 76 // Get . 77 func (t *Tran) Get(lang i18n.LanguageCodes, key, def string) string { 78 text := t.getText(lang, key) 79 if len(text) > 0 { 80 return text 81 } 82 return def 83 } 84 85 // Text . 86 func (t *Tran) Text(lang i18n.LanguageCodes, key string) string { 87 text := t.getText(lang, key) 88 if len(text) > 0 { 89 return text 90 } 91 return key 92 } 93 94 // Sprintf . 95 func (t *Tran) Sprintf(lang i18n.LanguageCodes, key string, args ...interface{}) string { 96 return fmt.Sprintf(t.escape(lang, key), args...) 97 } 98 99 func (t *Tran) getText(langs i18n.LanguageCodes, key string) string { 100 key = strings.ToLower(key) 101 for _, lang := range langs { 102 if t.dic != nil { 103 text := t.dic[lang.Code] 104 if text != nil { 105 if value, ok := text[key]; ok { 106 return value 107 } 108 } 109 text = t.dic[lang.RestrictedCode()] 110 if text != nil { 111 if value, ok := text[key]; ok { 112 return value 113 } 114 } 115 } 116 } 117 return "" 118 } 119 120 var regExp = regexp.MustCompile(`\$\{([^:}]*)(:[^}]*)?\}`) 121 122 func (t *Tran) escape(lang i18n.LanguageCodes, text string) string { 123 contents := reflectx.StringToBytes(text) 124 params := regExp.FindAllSubmatch(contents, -1) 125 for _, param := range params { 126 if len(param) != 3 { 127 continue 128 } 129 var key, defval []byte = param[1], nil 130 if len(param[2]) > 0 { 131 defval = param[2][1:] 132 } 133 k := reflectx.BytesToString(key) 134 val := t.getText(lang, k) 135 if len(val) <= 0 { 136 val = strings.Trim(reflectx.BytesToString(defval), `"`) 137 } 138 contents = bytes.Replace(contents, param[0], reflectx.StringToBytes(val), 1) 139 } 140 return reflectx.BytesToString(contents) 141 } 142 143 func loadToDic(file string, dic map[string]map[string]string) error { 144 m := make(map[string]interface{}) 145 err := cfg.LoadToMap(file, m) 146 if err != nil { 147 return fmt.Errorf("fail to load i18n file: %s", err) 148 } 149 for lang, v := range m { 150 text := dic[lang] 151 if text == nil { 152 text = make(map[string]string) 153 dic[lang] = text 154 } 155 switch m := v.(type) { 156 case map[string]string: 157 for k, v := range m { 158 text[strings.ToLower(k)] = fmt.Sprint(v) 159 } 160 case map[string]interface{}: 161 for k, v := range m { 162 text[strings.ToLower(k)] = fmt.Sprint(v) 163 } 164 case map[interface{}]interface{}: 165 for k, v := range m { 166 text[strings.ToLower(fmt.Sprint(k))] = fmt.Sprint(v) 167 } 168 default: 169 return fmt.Errorf("invalid i18n file format: %s", file) 170 } 171 } 172 return nil 173 }