i18n.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package i18n
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "time"
  8. "golang.org/x/text/language"
  9. )
  10. type ModuleKey string
  11. type Builder struct {
  12. supportLanguages []language.Tag
  13. getSupportLanguagesFromRequestFunc func(R *http.Request) []language.Tag
  14. moduleMessages map[language.Tag]context.Context
  15. matcher language.Matcher
  16. cookieName string
  17. queryName string
  18. }
  19. type Messages interface {
  20. }
  21. func New() *Builder {
  22. b := &Builder{
  23. supportLanguages: []language.Tag{
  24. language.English,
  25. },
  26. moduleMessages: map[language.Tag]context.Context{language.English: context.TODO()},
  27. cookieName: "lang",
  28. queryName: "lang",
  29. }
  30. b.matcher = language.NewMatcher(b.supportLanguages)
  31. return b
  32. }
  33. func (b *Builder) defaultLanguage() language.Tag {
  34. return b.supportLanguages[0]
  35. }
  36. func (b *Builder) GetCookieName() string {
  37. return b.cookieName
  38. }
  39. func (b *Builder) GetQueryName() string {
  40. return b.queryName
  41. }
  42. func (b *Builder) SupportLanguages(vs ...language.Tag) (r *Builder) {
  43. if len(vs) == 0 {
  44. panic("have to support at least one language")
  45. }
  46. b.supportLanguages = vs
  47. for _, l := range b.supportLanguages {
  48. if b.moduleMessages[l] == nil {
  49. b.moduleMessages[l] = context.TODO()
  50. }
  51. }
  52. b.matcher = language.NewMatcher(b.supportLanguages)
  53. return b
  54. }
  55. func (b *Builder) GetSupportLanguages() []language.Tag {
  56. return b.supportLanguages
  57. }
  58. func (b *Builder) GetSupportLanguagesFromRequest(R *http.Request) []language.Tag {
  59. if b.getSupportLanguagesFromRequestFunc != nil {
  60. return b.getSupportLanguagesFromRequestFunc(R)
  61. }
  62. return b.GetSupportLanguages()
  63. }
  64. func (b *Builder) GetSupportLanguagesFromRequestFunc(v func(R *http.Request) []language.Tag) (r *Builder) {
  65. b.getSupportLanguagesFromRequestFunc = v
  66. return b
  67. }
  68. func (b *Builder) RegisterForModule(lang language.Tag, module ModuleKey, msg Messages) (r *Builder) {
  69. c := b.moduleMessages[lang]
  70. if c == nil {
  71. c = context.TODO()
  72. }
  73. c = context.WithValue(c, module, msg)
  74. b.moduleMessages[lang] = c
  75. return b
  76. }
  77. func MustGetModuleMessages(r *http.Request, module ModuleKey, defaultMessages Messages) Messages {
  78. v := r.Context().Value(moduleMessagesKey)
  79. if v == nil {
  80. return defaultMessages
  81. }
  82. msg := v.(context.Context).Value(module)
  83. if msg == nil {
  84. msg = defaultMessages
  85. }
  86. return msg
  87. }
  88. type i18nContextKey int
  89. const (
  90. moduleMessagesKey i18nContextKey = iota
  91. dynaBuilderKey
  92. )
  93. func (b *Builder) EnsureLanguage(in http.Handler) (out http.Handler) {
  94. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  95. var lang = ""
  96. lang = r.FormValue(b.queryName)
  97. if len(lang) > 0 {
  98. maxAge := 365 * 24 * 60 * 60
  99. http.SetCookie(w, &http.Cookie{
  100. Name: b.cookieName,
  101. Value: lang,
  102. Path: "/",
  103. MaxAge: maxAge,
  104. Expires: time.Now().Add(time.Duration(maxAge) * time.Second),
  105. })
  106. } else {
  107. lang = b.GetCurrentLangFromCookie(r)
  108. }
  109. accept := r.Header.Get("Accept-Language")
  110. var availableLanguages []language.Tag
  111. var matcher language.Matcher
  112. if len(lang) > 0 {
  113. availableLanguages = b.GetSupportLanguages()
  114. matcher = b.matcher
  115. } else {
  116. availableLanguages = b.GetSupportLanguagesFromRequest(r)
  117. matcher = language.NewMatcher(availableLanguages)
  118. }
  119. _, i := language.MatchStrings(matcher, lang, accept)
  120. tag := availableLanguages[i]
  121. moduleMsgs := b.moduleMessages[tag]
  122. if moduleMsgs == nil {
  123. moduleMsgs = b.moduleMessages[b.defaultLanguage()]
  124. }
  125. if moduleMsgs == nil {
  126. panic(fmt.Sprintf("language %s not supported", tag.String()))
  127. }
  128. dyna := DynaNew().Language(tag.String())
  129. ctx := context.WithValue(r.Context(), moduleMessagesKey, moduleMsgs)
  130. ctx = context.WithValue(ctx, dynaBuilderKey, dyna)
  131. in.ServeHTTP(w, r.WithContext(ctx))
  132. if dyna.HaveMissingKeys() {
  133. log.Println(dyna.PrettyMissingKeys())
  134. }
  135. })
  136. }
  137. func (b *Builder) GetCurrentLangFromCookie(r *http.Request) (lang string) {
  138. langCookie, _ := r.Cookie(b.cookieName)
  139. if langCookie != nil {
  140. lang = langCookie.Value
  141. }
  142. return
  143. }