Reputation: 1
I'm trying to build a translation feature for my webapp. There are multiple packages in my app. Each package(directory) contains a translation folder and yaml files inside. But I have a problem with parsing and assign it to messages.
en.yaml
msgLogin : "You've login successfully"
msgProducts:
0: "You don't have any product."
1: "You have %d product."
2: "You have %d products."
errLogin: "Wrong password or username"
my code:
type TranslationEntry struct {
Key struct {
Value interface{}
}
}
func parseTranslations(dir string) {
files, _ := ioutil.ReadDir(dir)
for _, f := range files {
yamlFile, _ := ioutil.ReadFile(dir + "/" + f.Name())
var data translation
if err := yaml.Unmarshal(yamlFile, &data); err != nil {
return nil, err
}
lang := strings.Split(f.Name(), ".")[0]
switch msg := data.Key.Value.(type) {
case string:
message.SetString(language.Make(lang), cast.ToString(data.Key), cast.ToString(data.Key.Value))
case map[int]string:
message.Set(language.Make(lang), cast.ToString(data.Key),
plural.Selectf(1, "%d",
"=0", cast.ToString(data.Key.Value["0"]),
"=1", cast.ToString(data.Key.Value["1"]),
"=2", cast.ToString(data.Key.Value["2"]),
))
}
translations[lang] = &dictionary{Data: data}
}
}
I'm totally lost about how to design my struct or parse yaml file.
Thank you in advanced
Upvotes: 0
Views: 2189
Reputation: 1530
If you're using the YAML library I think you're using (https://godoc.org/gopkg.in/yaml.v2), to make a Golang struct which can use Unmarshal to map from the YAML file in your post you can do this:
type TranslationEntry struct {
MsgLogin string `yaml:"msgLogin"`
MsgProducts map[int]string `yaml:"msgProducts"`
ErrLogin string `yaml:"errLogin"`
}
The things inside the `` after the field declarations are called tags. They're the way field names are usually specified when mapping between some datatype and a Golang struct (in my case usually I map a struct to JSON, but I've also done YAML). If you're using the same YAML parser I mentioned above, this is how it works.
Basically the text inside the double quotes is the YAML key to which your struct field will be mapped. In the above code the only difference between the YAML key name and the struct field name is capitalization, but here is an example using totally different names:
type ExampleStruct struct {
MyAbcField string `yaml:"abc"`
}
This will set the value of MyAbcField
to "my data"
when using Unmarshal with ExampleStruct
and the following YAML:
abc: "my data"
This allows you to design a Golang struct which matches however you decide to structure your YAML.
Here's my above code in Go Playground: https://play.golang.org/p/Q9FvNsw-BOx
Now, if you are unable to fix a structure for your YAML files, you can also parse into nested maps. You can do this by passing a variable of type interface{}
(empty interface) to Unmarshal instead of a struct. However, this requires a lot of boilerplate because you will need to do type assertions to access your data. Thus I recommend using a fixed structure instead unless you absolutely can't avoid it.
Here's an example where I parse the YAML you posted and then get the msgLogin
field:
var data interface{}
if err := yaml.Unmarshal([]byte(yamlFile), &data); err != nil {
// handle error
}
// a type assertion that data is a map is needed in order to get keys or iterate
topLevel, ok := data.(map[interface{}]interface{})
if !ok {
// handle error
}
fmt.Println(topLevel["msgLogin"])
And here's the Go Playground of my struct example changed to use parsing into a nested map instead: https://play.golang.org/p/ERBjClSazkz
Upvotes: 1