You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
5.2 KiB

  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.LoadSources(ini.LoadOptions{
  134. IgnoreInlineComment: true,
  135. UnescapeValueCommentSymbols: true,
  136. }, localeFile, otherLocaleFiles...)
  137. if err == nil {
  138. message.BlockMode = false
  139. lc := new(locale)
  140. lc.lang = lang
  141. lc.langDesc = langDesc
  142. lc.message = message
  143. if locales.Add(lc) == false {
  144. return ErrLangAlreadyExist
  145. }
  146. }
  147. return err
  148. }
  149. // SetMessage sets the message file for localization.
  150. func SetMessage(lang string, localeFile interface{}, otherLocaleFiles ...interface{}) error {
  151. return SetMessageWithDesc(lang, lang, localeFile, otherLocaleFiles...)
  152. }
  153. // Locale represents the information of localization.
  154. type Locale struct {
  155. Lang string
  156. }
  157. // Tr translates content to target language.
  158. func (l Locale) Tr(format string, args ...interface{}) string {
  159. return Tr(l.Lang, format, args...)
  160. }
  161. // Index returns lang index of LangStore.
  162. func (l Locale) Index() int {
  163. return IndexLang(l.Lang)
  164. }
  165. // Tr translates content to target language.
  166. func Tr(lang, format string, args ...interface{}) string {
  167. var section string
  168. idx := strings.IndexByte(format, '.')
  169. if idx > 0 {
  170. section = format[:idx]
  171. format = format[idx+1:]
  172. }
  173. value, ok := locales.Get(lang, section, format)
  174. if ok {
  175. format = value
  176. }
  177. if len(args) > 0 {
  178. params := make([]interface{}, 0, len(args))
  179. for _, arg := range args {
  180. if arg == nil {
  181. continue
  182. }
  183. val := reflect.ValueOf(arg)
  184. if val.Kind() == reflect.Slice {
  185. for i := 0; i < val.Len(); i++ {
  186. params = append(params, val.Index(i).Interface())
  187. }
  188. } else {
  189. params = append(params, arg)
  190. }
  191. }
  192. return fmt.Sprintf(format, params...)
  193. }
  194. return format
  195. }