i18n.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright 2013 Unknwon
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // 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, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. // Package i18n is for app Internationalization and Localization.
  15. package i18n
  16. import (
  17. "errors"
  18. "fmt"
  19. "reflect"
  20. "strings"
  21. "gopkg.in/ini.v1"
  22. )
  23. var (
  24. ErrLangAlreadyExist = errors.New("Lang already exists")
  25. locales = &localeStore{store: make(map[string]*locale)}
  26. )
  27. type locale struct {
  28. id int
  29. lang string
  30. langDesc string
  31. message *ini.File
  32. }
  33. type localeStore struct {
  34. langs []string
  35. langDescs []string
  36. store map[string]*locale
  37. defaultLang string
  38. }
  39. // Get target language string
  40. func (d *localeStore) Get(lang, section, format string) (string, bool) {
  41. if locale, ok := d.store[lang]; ok {
  42. if key, err := locale.message.Section(section).GetKey(format); err == nil {
  43. return key.Value(), true
  44. }
  45. }
  46. if len(d.defaultLang) > 0 && lang != d.defaultLang {
  47. return d.Get(d.defaultLang, section, format)
  48. }
  49. return "", false
  50. }
  51. func (d *localeStore) Add(lc *locale) bool {
  52. if _, ok := d.store[lc.lang]; ok {
  53. return false
  54. }
  55. lc.id = len(d.langs)
  56. d.langs = append(d.langs, lc.lang)
  57. d.langDescs = append(d.langDescs, lc.langDesc)
  58. d.store[lc.lang] = lc
  59. return true
  60. }
  61. func (d *localeStore) Reload(langs ...string) (err error) {
  62. if len(langs) == 0 {
  63. for _, lc := range d.store {
  64. if err = lc.message.Reload(); err != nil {
  65. return err
  66. }
  67. }
  68. } else {
  69. for _, lang := range langs {
  70. if lc, ok := d.store[lang]; ok {
  71. if err = lc.message.Reload(); err != nil {
  72. return err
  73. }
  74. }
  75. }
  76. }
  77. return nil
  78. }
  79. // SetDefaultLang sets default language which is a indicator that
  80. // when target language is not found, try find in default language again.
  81. func SetDefaultLang(lang string) {
  82. locales.defaultLang = lang
  83. }
  84. // ReloadLangs reloads locale files.
  85. func ReloadLangs(langs ...string) error {
  86. return locales.Reload(langs...)
  87. }
  88. // Count returns number of languages that are registered.
  89. func Count() int {
  90. return len(locales.langs)
  91. }
  92. // ListLangs returns list of all locale languages.
  93. func ListLangs() []string {
  94. langs := make([]string, len(locales.langs))
  95. copy(langs, locales.langs)
  96. return langs
  97. }
  98. func ListLangDescs() []string {
  99. langDescs := make([]string, len(locales.langDescs))
  100. copy(langDescs, locales.langDescs)
  101. return langDescs
  102. }
  103. // IsExist returns true if given language locale exists.
  104. func IsExist(lang string) bool {
  105. _, ok := locales.store[lang]
  106. return ok
  107. }
  108. // IndexLang returns index of language locale,
  109. // it returns -1 if locale not exists.
  110. func IndexLang(lang string) int {
  111. if lc, ok := locales.store[lang]; ok {
  112. return lc.id
  113. }
  114. return -1
  115. }
  116. // GetLangByIndex return language by given index.
  117. func GetLangByIndex(index int) string {
  118. if index < 0 || index >= len(locales.langs) {
  119. return ""
  120. }
  121. return locales.langs[index]
  122. }
  123. func GetDescriptionByIndex(index int) string {
  124. if index < 0 || index >= len(locales.langDescs) {
  125. return ""
  126. }
  127. return locales.langDescs[index]
  128. }
  129. func GetDescriptionByLang(lang string) string {
  130. return GetDescriptionByIndex(IndexLang(lang))
  131. }
  132. func SetMessageWithDesc(lang, langDesc string, localeFile interface{}, otherLocaleFiles ...interface{}) error {
  133. message, err := ini.Load(localeFile, otherLocaleFiles...)
  134. if err == nil {
  135. message.BlockMode = false
  136. lc := new(locale)
  137. lc.lang = lang
  138. lc.langDesc = langDesc
  139. lc.message = message
  140. if locales.Add(lc) == false {
  141. return ErrLangAlreadyExist
  142. }
  143. }
  144. return err
  145. }
  146. // SetMessage sets the message file for localization.
  147. func SetMessage(lang string, localeFile interface{}, otherLocaleFiles ...interface{}) error {
  148. return SetMessageWithDesc(lang, lang, localeFile, otherLocaleFiles...)
  149. }
  150. // Locale represents the information of localization.
  151. type Locale struct {
  152. Lang string
  153. }
  154. // Tr translates content to target language.
  155. func (l Locale) Tr(format string, args ...interface{}) string {
  156. return Tr(l.Lang, format, args...)
  157. }
  158. // Index returns lang index of LangStore.
  159. func (l Locale) Index() int {
  160. return IndexLang(l.Lang)
  161. }
  162. // Tr translates content to target language.
  163. func Tr(lang, format string, args ...interface{}) string {
  164. var section string
  165. idx := strings.IndexByte(format, '.')
  166. if idx > 0 {
  167. section = format[:idx]
  168. format = format[idx+1:]
  169. }
  170. value, ok := locales.Get(lang, section, format)
  171. if ok {
  172. format = value
  173. }
  174. if len(args) > 0 {
  175. params := make([]interface{}, 0, len(args))
  176. for _, arg := range args {
  177. if arg == nil {
  178. continue
  179. }
  180. val := reflect.ValueOf(arg)
  181. if val.Kind() == reflect.Slice {
  182. for i := 0; i < val.Len(); i++ {
  183. params = append(params, val.Index(i).Interface())
  184. }
  185. } else {
  186. params = append(params, arg)
  187. }
  188. }
  189. return fmt.Sprintf(format, params...)
  190. }
  191. return format
  192. }