# 10.3 International sites The previous section explained how to deal with localized resources, namely by using locale configuration files. So what can we do if we need to deal with *multiple* localized resources like text translations, times and dates, numbers, etc? This section will address these issues one by one. ## Managing multiple locale packages In the development of an application, often the first thing you need to do is to decide whether or not you want to support more than one language. If you do decide to support multiple languages, you'll need to develop an organizational structure to facilitate the process of adding more languages in the future. One way we can do this is to put all our related locale files together in a `config/locales` directory, or something of the like. Let's suppose you want to support both Chinese and English. In this case, you'd be placing both the en.json and zh.json locale files into the aforementioned folder. Their contents would probably look something like the following: # zh.json { "zh": { "submit": "提交", "create": "创建" } } # en.json { "en": { "submit": "Submit", "create": "Create" } } We decided to use some 3rd party Go packages to help us internationalize our web applications. In the case of [go-i18n](https://github.com/astaxie/go-i18n) ( ***A more advanced i18n package can be found [here](https://github.com/beego/i18n)*** ), we first have to register our `config/locales` directory to load all of our locale files: Tr := i18n.NewLocale() Tr.LoadPath("config/locales") This package is simple to use. We can test that it works like so: fmt.Println(Tr.Translate("submit")) //Output "submit" Tr.SetLocale("zn") fmt.Println(Tr.Translate("submit")) //Outputs "递交" ## Automatically load local package We've just described how to automatically load custom language packs. In fact, the `go-i18n` library comes pre-loaded with a bunch of default formatting information such as time and currency formats. These default configurations can be overridden and customized by users to suit their needs. Consider the following process: //Load the default configuration files, which are placed below in `go-i18n/locales` //File should be named zh.json, en-json, en-US.json etc., so we can continuously support more languages func (il *IL) loadDefaultTranslations(dirPath string) error { dir, err := os.Open(dirPath) if err != nil { return err } defer dir.Close() names, err := dir.Readdirnames(-1) if err != nil { return err } for _, name := range names { fullPath := path.Join(dirPath, name) fi, err := os.Stat(fullPath) if err != nil { return err } if fi.IsDir() { if err := il.loadTranslations(fullPath); err != nil { return err } } else if locale := il.matchingLocaleFromFileName(name); locale != "" { file, err := os.Open(fullPath) if err != nil { return err } defer file.Close() if err := il.loadTranslation(file, locale); err != nil { return err } } } return nil } Using the above code to load all of our default translations, we can then use the following code to select and use a locale: fmt.Println(Tr.Time(time.Now())) //Output: 2009年1月08日 星期四 20:37:58 CST fmt.Println(Tr.Time(time.Now(),"long")) //Output: 2009年1月08日 fmt.Println(Tr.Money(11.11)) //Output: ¥11.11 ## Template mapfunc Above, we've presented one way of managing and integrating a number of language packs. Some of the functions we've implemented are based on the logical layer, for example: "Tr.Translate", "Tr.Time", "Tr.Money" and so on. In the logical layer, we can use these functions (after supplying the required parameters) for applying your translations, outputting the results directly to the template layer at render time. What can we do if we want to use these functions *directly* in the template layer? In case you've forgotten, earlier in the book we mentioned that Go templates support custom template functions. The following code shows how easy mapfunc is to implement: 1 text information A simple text conversion function implementing a mapfunc can be seen below. It uses `Tr.Translate` to perform the appropriate translations: func I18nT(args ...interface{}) string { ok := false var s string if len(args) == 1 { s, ok = args[0].(string) } if !ok { s = fmt.Sprint(args...) } return Tr.Translate(s) } We register the function like so: t.Funcs(template.FuncMap{"T": I18nT}) Then use it from your template: {{.V.Submit | T}} 2. The date and time Dates and times call the `Tr.Time` function to perform their translations. The mapfunc is implemented as follows: func I18nTimeDate(args ...interface{}) string { ok := false var s string if len(args) == 1 { s, ok = args[0].(string) } if !ok { s = fmt.Sprint(args...) } return Tr.Time(s) } Register the function like so: t.Funcs(template.FuncMap{"TD": I18nTimeDate}) Then use it from your template: {{.V.Now | TD}} 3 Currency Information Currencies use the `Tr.Money` function to convert money. The mapFunc is implemented as follows: func I18nMoney(args ...interface{}) string { ok := false var s string if len(args) == 1 { s, ok = args[0].(string) } if !ok { s = fmt.Sprint(args...) } return Tr.Money(s) } Register the function like so: t.Funcs(template.FuncMap{"M": I18nMoney}) Then use it from your template: {{.V.Money | M}} ## Summary In this section we learned how to implement multiple language packs in our web applications. We saw that through custom language packs, we can not only easily internationalize our applications, but facilitate the addition of other languages also (through the use of a configuration file). By default, the go-i18n package will provide some common configurations for time, currency, etc., which can be very convenient to use. We learned that these functions can also be used directly from our templates using mapping functions; each translated string can be piped directly to our templates. This enables our web applications to accommodate multiple languages with minimal effort. ## Links - [Directory](preface.md) - Previous section: [Localized resources](10.2.md) - Next section: [Summary](10.4.md)