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.

translation.go 6.4KB

Append `(comment)` when a link points at a comment rather than the whole issue (#23734) Close #23671 For the feature mentioned above, this PR append ' (comment)' to the rendered html if it is a hashcomment. After the PR, type in the following ``` pull request from other repo: http://localhost:3000/testOrg/testOrgRepo/pulls/2 pull request from this repo: http://localhost:3000/aaa/testA/pulls/2 issue comment from this repo: http://localhost:3000/aaa/testA/issues/1#issuecomment-18 http://localhost:3000/aaa/testA/pulls/2#issue-9 issue comment from other repo: http://localhost:3000/testOrg/testOrgRepo/pulls/2#issuecomment-24 http://localhost:3000/testOrg/testOrgRepo/pulls/2#issue ``` Gives: <img width="687" alt="截屏2023-03-27 13 53 06" src="https://user-images.githubusercontent.com/17645053/227852387-2b218e0d-3468-4d90-ad81-d702ddd17fd2.png"> Other than the above feature, this PR also includes two other changes: 1 Right now, the render of links from file changed tab in pull request might not be very proper, for example, if type in the following. (not sure if this is an issue or design, if not an issue, I will revert the changes). example on [try.gitea.io](https://try.gitea.io/HesterG/testrepo/pulls/1) ``` https://try.gitea.io/HesterG/testrepo/pulls/1/files#issuecomment-162725 https://try.gitea.io/HesterG/testrepo/pulls/1/files ``` it will render the following <img width="899" alt="截屏2023-03-24 15 41 37" src="https://user-images.githubusercontent.com/17645053/227456117-5eccedb7-9118-4540-929d-aee9a76de852.png"> In this PR, skip processing the link into a ref issue if it is a link from files changed tab in pull request After: type in following ``` hash comment on files changed tab: http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24 files changed link: http://localhost:3000/testOrg/testOrgRepo/pulls/2/files ``` Gives <img width="708" alt="截屏2023-03-27 22 09 02" src="https://user-images.githubusercontent.com/17645053/227964273-5dc06c50-3713-489c-b05d-d95367d0ab0f.png"> 2 Right now, after editing the comment area, there will not be tippys attached to `ref-issue`; and no tippy attached on preview as well. example: https://user-images.githubusercontent.com/17645053/227850540-5ae34e2d-b1d7-4d0d-9726-7701bf825d1f.mov In this PR, in frontend, make sure tippy is added after editing the comment, and to the comment on preview tab After: https://user-images.githubusercontent.com/17645053/227853777-06f56b4c-1148-467c-b6f7-f79418e67504.mov
1 year ago
Append `(comment)` when a link points at a comment rather than the whole issue (#23734) Close #23671 For the feature mentioned above, this PR append ' (comment)' to the rendered html if it is a hashcomment. After the PR, type in the following ``` pull request from other repo: http://localhost:3000/testOrg/testOrgRepo/pulls/2 pull request from this repo: http://localhost:3000/aaa/testA/pulls/2 issue comment from this repo: http://localhost:3000/aaa/testA/issues/1#issuecomment-18 http://localhost:3000/aaa/testA/pulls/2#issue-9 issue comment from other repo: http://localhost:3000/testOrg/testOrgRepo/pulls/2#issuecomment-24 http://localhost:3000/testOrg/testOrgRepo/pulls/2#issue ``` Gives: <img width="687" alt="截屏2023-03-27 13 53 06" src="https://user-images.githubusercontent.com/17645053/227852387-2b218e0d-3468-4d90-ad81-d702ddd17fd2.png"> Other than the above feature, this PR also includes two other changes: 1 Right now, the render of links from file changed tab in pull request might not be very proper, for example, if type in the following. (not sure if this is an issue or design, if not an issue, I will revert the changes). example on [try.gitea.io](https://try.gitea.io/HesterG/testrepo/pulls/1) ``` https://try.gitea.io/HesterG/testrepo/pulls/1/files#issuecomment-162725 https://try.gitea.io/HesterG/testrepo/pulls/1/files ``` it will render the following <img width="899" alt="截屏2023-03-24 15 41 37" src="https://user-images.githubusercontent.com/17645053/227456117-5eccedb7-9118-4540-929d-aee9a76de852.png"> In this PR, skip processing the link into a ref issue if it is a link from files changed tab in pull request After: type in following ``` hash comment on files changed tab: http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24 files changed link: http://localhost:3000/testOrg/testOrgRepo/pulls/2/files ``` Gives <img width="708" alt="截屏2023-03-27 22 09 02" src="https://user-images.githubusercontent.com/17645053/227964273-5dc06c50-3713-489c-b05d-d95367d0ab0f.png"> 2 Right now, after editing the comment area, there will not be tippys attached to `ref-issue`; and no tippy attached on preview as well. example: https://user-images.githubusercontent.com/17645053/227850540-5ae34e2d-b1d7-4d0d-9726-7701bf825d1f.mov In this PR, in frontend, make sure tippy is added after editing the comment, and to the comment on preview tab After: https://user-images.githubusercontent.com/17645053/227853777-06f56b4c-1148-467c-b6f7-f79418e67504.mov
1 year ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package translation
  4. import (
  5. "context"
  6. "html/template"
  7. "sort"
  8. "strings"
  9. "sync"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/options"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/modules/translation/i18n"
  14. "code.gitea.io/gitea/modules/util"
  15. "golang.org/x/text/language"
  16. "golang.org/x/text/message"
  17. "golang.org/x/text/number"
  18. )
  19. type contextKey struct{}
  20. var ContextKey any = &contextKey{}
  21. // Locale represents an interface to translation
  22. type Locale interface {
  23. Language() string
  24. TrString(string, ...any) string
  25. Tr(key string, args ...any) template.HTML
  26. TrN(cnt any, key1, keyN string, args ...any) template.HTML
  27. PrettyNumber(v any) string
  28. }
  29. // LangType represents a lang type
  30. type LangType struct {
  31. Lang, Name string // these fields are used directly in templates: {{range .AllLangs}}{{.Lang}}{{.Name}}{{end}}
  32. }
  33. var (
  34. lock *sync.RWMutex
  35. allLangs []*LangType
  36. allLangMap map[string]*LangType
  37. matcher language.Matcher
  38. supportedTags []language.Tag
  39. )
  40. // AllLangs returns all supported languages sorted by name
  41. func AllLangs() []*LangType {
  42. return allLangs
  43. }
  44. // InitLocales loads the locales
  45. func InitLocales(ctx context.Context) {
  46. if lock != nil {
  47. lock.Lock()
  48. defer lock.Unlock()
  49. } else if !setting.IsProd && lock == nil {
  50. lock = &sync.RWMutex{}
  51. }
  52. refreshLocales := func() {
  53. i18n.ResetDefaultLocales()
  54. localeNames, err := options.AssetFS().ListFiles("locale", true)
  55. if err != nil {
  56. log.Fatal("Failed to list locale files: %v", err)
  57. }
  58. localeData := make(map[string][]byte, len(localeNames))
  59. for _, name := range localeNames {
  60. localeData[name], err = options.Locale(name)
  61. if err != nil {
  62. log.Fatal("Failed to load %s locale file. %v", name, err)
  63. }
  64. }
  65. supportedTags = make([]language.Tag, len(setting.Langs))
  66. for i, lang := range setting.Langs {
  67. supportedTags[i] = language.Raw.Make(lang)
  68. }
  69. matcher = language.NewMatcher(supportedTags)
  70. for i := range setting.Names {
  71. var localeDataBase []byte
  72. if i == 0 && setting.Langs[0] != "en-US" {
  73. // Only en-US has complete translations. When use other language as default, the en-US should still be used as fallback.
  74. localeDataBase = localeData["locale_en-US.ini"]
  75. if localeDataBase == nil {
  76. log.Fatal("Failed to load locale_en-US.ini file.")
  77. }
  78. }
  79. key := "locale_" + setting.Langs[i] + ".ini"
  80. if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localeDataBase, localeData[key]); err != nil {
  81. log.Error("Failed to set messages to %s: %v", setting.Langs[i], err)
  82. }
  83. }
  84. if len(setting.Langs) != 0 {
  85. defaultLangName := setting.Langs[0]
  86. if defaultLangName != "en-US" {
  87. log.Info("Use the first locale (%s) in LANGS setting option as default", defaultLangName)
  88. }
  89. i18n.DefaultLocales.SetDefaultLang(defaultLangName)
  90. }
  91. }
  92. refreshLocales()
  93. langs, descs := i18n.DefaultLocales.ListLangNameDesc()
  94. allLangs = make([]*LangType, 0, len(langs))
  95. allLangMap = map[string]*LangType{}
  96. for i, v := range langs {
  97. l := &LangType{v, descs[i]}
  98. allLangs = append(allLangs, l)
  99. allLangMap[v] = l
  100. }
  101. // Sort languages case-insensitive according to their name - needed for the user settings
  102. sort.Slice(allLangs, func(i, j int) bool {
  103. return strings.ToLower(allLangs[i].Name) < strings.ToLower(allLangs[j].Name)
  104. })
  105. if !setting.IsProd {
  106. go options.AssetFS().WatchLocalChanges(ctx, func() {
  107. lock.Lock()
  108. defer lock.Unlock()
  109. refreshLocales()
  110. })
  111. }
  112. }
  113. // Match matches accept languages
  114. func Match(tags ...language.Tag) language.Tag {
  115. _, i, _ := matcher.Match(tags...)
  116. return supportedTags[i]
  117. }
  118. // locale represents the information of localization.
  119. type locale struct {
  120. i18n.Locale
  121. Lang, LangName string // these fields are used directly in templates: ctx.Locale.Lang
  122. msgPrinter *message.Printer
  123. }
  124. var _ Locale = (*locale)(nil)
  125. // NewLocale return a locale
  126. func NewLocale(lang string) Locale {
  127. if lock != nil {
  128. lock.RLock()
  129. defer lock.RUnlock()
  130. }
  131. langName := "unknown"
  132. if l, ok := allLangMap[lang]; ok {
  133. langName = l.Name
  134. } else if len(setting.Langs) > 0 {
  135. lang = setting.Langs[0]
  136. langName = setting.Names[0]
  137. }
  138. i18nLocale, _ := i18n.GetLocale(lang)
  139. l := &locale{
  140. Locale: i18nLocale,
  141. Lang: lang,
  142. LangName: langName,
  143. }
  144. if langTag, err := language.Parse(lang); err != nil {
  145. log.Error("Failed to parse language tag from name %q: %v", l.Lang, err)
  146. l.msgPrinter = message.NewPrinter(language.English)
  147. } else {
  148. l.msgPrinter = message.NewPrinter(langTag)
  149. }
  150. return l
  151. }
  152. func (l *locale) Language() string {
  153. return l.Lang
  154. }
  155. // Language specific rules for translating plural texts
  156. var trNLangRules = map[string]func(int64) int{
  157. // the default rule is "en-US" if a language isn't listed here
  158. "en-US": func(cnt int64) int {
  159. if cnt == 1 {
  160. return 0
  161. }
  162. return 1
  163. },
  164. "lv-LV": func(cnt int64) int {
  165. if cnt%10 == 1 && cnt%100 != 11 {
  166. return 0
  167. }
  168. return 1
  169. },
  170. "ru-RU": func(cnt int64) int {
  171. if cnt%10 == 1 && cnt%100 != 11 {
  172. return 0
  173. }
  174. return 1
  175. },
  176. "zh-CN": func(cnt int64) int {
  177. return 0
  178. },
  179. "zh-HK": func(cnt int64) int {
  180. return 0
  181. },
  182. "zh-TW": func(cnt int64) int {
  183. return 0
  184. },
  185. "fr-FR": func(cnt int64) int {
  186. if cnt > -2 && cnt < 2 {
  187. return 0
  188. }
  189. return 1
  190. },
  191. }
  192. func (l *locale) Tr(s string, args ...any) template.HTML {
  193. return l.TrHTML(s, args...)
  194. }
  195. // TrN returns translated message for plural text translation
  196. func (l *locale) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
  197. var c int64
  198. if t, ok := cnt.(int); ok {
  199. c = int64(t)
  200. } else if t, ok := cnt.(int16); ok {
  201. c = int64(t)
  202. } else if t, ok := cnt.(int32); ok {
  203. c = int64(t)
  204. } else if t, ok := cnt.(int64); ok {
  205. c = t
  206. } else {
  207. return l.Tr(keyN, args...)
  208. }
  209. ruleFunc, ok := trNLangRules[l.Lang]
  210. if !ok {
  211. ruleFunc = trNLangRules["en-US"]
  212. }
  213. if ruleFunc(c) == 0 {
  214. return l.Tr(key1, args...)
  215. }
  216. return l.Tr(keyN, args...)
  217. }
  218. func (l *locale) PrettyNumber(v any) string {
  219. // TODO: this mechanism is not good enough, the complete solution is to switch the translation system to ICU message format
  220. if s, ok := v.(string); ok {
  221. if num, err := util.ToInt64(s); err == nil {
  222. v = num
  223. } else if num, err := util.ToFloat64(s); err == nil {
  224. v = num
  225. }
  226. }
  227. return l.msgPrinter.Sprintf("%v", number.Decimal(v))
  228. }
  229. func init() {
  230. // prepare a default matcher, especially for tests
  231. supportedTags = []language.Tag{language.English}
  232. matcher = language.NewMatcher(supportedTags)
  233. }