http.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. package detect
  2. import (
  3. "net/http"
  4. "strings"
  5. )
  6. /* NewHttpDetector initializes new language detector */
  7. func NewHttpDetector(languages []string, path string, maxAge int) func(http.ResponseWriter, *http.Request) string {
  8. const cookieName = "lang"
  9. if len(languages) == 0 {
  10. panic("empty languages list")
  11. }
  12. /* initialize state */
  13. defaultLang := strings.ToLower(languages[0])
  14. langSet := make(map[string]struct{}, len(languages))
  15. for _, lang := range languages {
  16. langSet[strings.ToLower(lang)] = struct{}{}
  17. }
  18. /* detectLanguage returns a language string */
  19. return func(w http.ResponseWriter, r *http.Request) string {
  20. /* check for query language override */
  21. if lang := strings.ToLower(r.URL.Query().Get(cookieName)); lang != "" {
  22. if _, ok := langSet[lang]; ok {
  23. /* language provided by query string, enforce it and send cookie */
  24. http.SetCookie(w, &http.Cookie{
  25. Name: cookieName,
  26. Value: lang,
  27. Path: path,
  28. HttpOnly: true,
  29. SameSite: http.SameSiteLaxMode,
  30. Secure: r.TLS != nil,
  31. MaxAge: maxAge,
  32. })
  33. return lang
  34. }
  35. }
  36. /* check for cookie preferences */
  37. cookie, err := r.Cookie(cookieName)
  38. if err == nil {
  39. cookieLang := strings.ToLower(cookie.Value)
  40. if _, ok := langSet[cookieLang]; ok {
  41. return cookieLang
  42. }
  43. }
  44. /* fall back to browser preferences */
  45. acceptLanguage := strings.ToLower(r.Header.Get("Accept-Language"))
  46. parts := strings.Split(acceptLanguage, ",")
  47. for _, part := range parts {
  48. part = strings.TrimSpace(part)
  49. if part == "" {
  50. continue
  51. }
  52. /* cut off ";q=..." */
  53. if idx := strings.IndexByte(part, ';'); idx >= 0 {
  54. part = part[:idx]
  55. }
  56. /* try exact match first */
  57. if _, ok := langSet[part]; ok {
  58. return part
  59. }
  60. /* try base language */
  61. if i := strings.IndexByte(part, '-'); i >= 0 {
  62. base := part[:i]
  63. if _, ok := langSet[base]; ok {
  64. return base
  65. }
  66. }
  67. }
  68. return defaultLang
  69. }
  70. }