i18n.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "sort"
  7. "github.com/knadh/listmonk/internal/i18n"
  8. "github.com/knadh/stuffbin"
  9. "github.com/labstack/echo"
  10. )
  11. type i18nLang struct {
  12. Code string `json:"code"`
  13. Name string `json:"name"`
  14. }
  15. type i18nLangRaw struct {
  16. Code string `json:"_.code"`
  17. Name string `json:"_.name"`
  18. }
  19. // handleGetI18nLang returns the JSON language pack given the language code.
  20. func handleGetI18nLang(c echo.Context) error {
  21. app := c.Get("app").(*App)
  22. lang := c.Param("lang")
  23. if len(lang) > 6 || reLangCode.MatchString(lang) {
  24. return echo.NewHTTPError(http.StatusBadRequest, "Invalid language code.")
  25. }
  26. i, ok, err := getI18nLang(lang, app.fs)
  27. if err != nil && !ok {
  28. return echo.NewHTTPError(http.StatusBadRequest, "Unknown language.")
  29. }
  30. return c.JSON(http.StatusOK, okResp{json.RawMessage(i.JSON())})
  31. }
  32. // getI18nLangList returns the list of available i18n languages.
  33. func getI18nLangList(lang string, app *App) ([]i18nLang, error) {
  34. list, err := app.fs.Glob("/i18n/*.json")
  35. if err != nil {
  36. return nil, err
  37. }
  38. var out []i18nLang
  39. for _, l := range list {
  40. b, err := app.fs.Get(l)
  41. if err != nil {
  42. return out, fmt.Errorf("error reading lang file: %s: %v", l, err)
  43. }
  44. var lang i18nLangRaw
  45. if err := json.Unmarshal(b.ReadBytes(), &lang); err != nil {
  46. return out, fmt.Errorf("error parsing lang file: %s: %v", l, err)
  47. }
  48. out = append(out, i18nLang{
  49. Code: lang.Code,
  50. Name: lang.Name,
  51. })
  52. }
  53. sort.SliceStable(out, func(i, j int) bool {
  54. return out[i].Code < out[j].Code
  55. })
  56. return out, nil
  57. }
  58. // The bool indicates whether the specified language could be loaded. If it couldn't
  59. // be, the app shouldn't halt but throw a warning.
  60. func getI18nLang(lang string, fs stuffbin.FileSystem) (*i18n.I18n, bool, error) {
  61. const def = "en"
  62. b, err := fs.Read(fmt.Sprintf("/i18n/%s.json", def))
  63. if err != nil {
  64. return nil, false, fmt.Errorf("error reading default i18n language file: %s: %v", def, err)
  65. }
  66. // Initialize with the default language.
  67. i, err := i18n.New(b)
  68. if err != nil {
  69. return nil, false, fmt.Errorf("error unmarshalling i18n language: %s: %v", lang, err)
  70. }
  71. // Load the selected language on top of it.
  72. b, err = fs.Read(fmt.Sprintf("/i18n/%s.json", lang))
  73. if err != nil {
  74. return i, true, fmt.Errorf("error reading i18n language file: %s: %v", lang, err)
  75. }
  76. if err := i.Load(b); err != nil {
  77. return i, true, fmt.Errorf("error loading i18n language file: %s: %v", lang, err)
  78. }
  79. return i, true, nil
  80. }