Refactor i18n, use Locale to provide i18n/translation related functions (#18648)

* remove unnecessary web context data fields, and unify the i18n/translation related functions to `Locale`
* in development, show an error if a translation key is missing
* remove the unnecessary loops `for _, lang := range translation.AllLangs()` for every request, which improves the performance slightly
* use `ctx.Locale.Language()` instead of `ctx.Data["Lang"].(string)`
* add more comments about how the Locale/LangType fields are used
This commit is contained in:
wxiaoguang 2022-02-08 11:02:30 +08:00 committed by GitHub
parent 7b25a010c8
commit a60e8be8d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 75 additions and 75 deletions

View file

@ -38,7 +38,6 @@ import (
"gitea.com/go-chi/session"
chi "github.com/go-chi/chi/v5"
"github.com/unknwon/com"
"github.com/unknwon/i18n"
"github.com/unrolled/render"
"golang.org/x/crypto/pbkdf2"
)
@ -738,15 +737,7 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Data["UnitProjectsGlobalDisabled"] = unit.TypeProjects.UnitGlobalDisabled()
ctx.Data["i18n"] = locale
ctx.Data["Tr"] = i18n.Tr
ctx.Data["Lang"] = locale.Language()
ctx.Data["AllLangs"] = translation.AllLangs()
for _, lang := range translation.AllLangs() {
if lang.Lang == locale.Language() {
ctx.Data["LangName"] = lang.Name
break
}
}
next.ServeHTTP(ctx.Resp, ctx.Req)

View file

@ -25,17 +25,18 @@ type Locale interface {
// LangType represents a lang type
type LangType struct {
Lang, Name string
Lang, Name string // these fields are used directly in templates: {{range .AllLangs}}{{.Lang}}{{.Name}}{{end}}
}
var (
matcher language.Matcher
allLangs []LangType
allLangs []*LangType
allLangMap map[string]*LangType
supportedTags []language.Tag
)
// AllLangs returns all supported languages sorted by name
func AllLangs() []LangType {
func AllLangs() []*LangType {
return allLangs
}
@ -81,14 +82,17 @@ func InitLocales() {
}
i18n.SetDefaultLang("en-US")
allLangs = make([]LangType, 0, i18n.Count()-1)
allLangs = make([]*LangType, 0, i18n.Count())
allLangMap = map[string]*LangType{}
langs := i18n.ListLangs()
names := i18n.ListLangDescs()
descs := i18n.ListLangDescs()
for i, v := range langs {
allLangs = append(allLangs, LangType{v, names[i]})
l := &LangType{v, descs[i]}
allLangs = append(allLangs, l)
allLangMap[v] = l
}
// Sort languages case insensitive according to their name - needed for the user settings
// Sort languages case-insensitive according to their name - needed for the user settings
sort.Slice(allLangs, func(i, j int) bool {
return strings.ToLower(allLangs[i].Name) < strings.ToLower(allLangs[j].Name)
})
@ -102,13 +106,18 @@ func Match(tags ...language.Tag) language.Tag {
// locale represents the information of localization.
type locale struct {
Lang string
Lang, LangName string // these fields are used directly in templates: .i18n.Lang
}
// NewLocale return a locale
func NewLocale(lang string) Locale {
langName := "unknown"
if l, ok := allLangMap[lang]; ok {
langName = l.Name
}
return &locale{
Lang: lang,
Lang: lang,
LangName: langName,
}
}
@ -118,7 +127,16 @@ func (l *locale) Language() string {
// Tr translates content to target language.
func (l *locale) Tr(format string, args ...interface{}) string {
return i18n.Tr(l.Lang, format, args...)
if setting.IsProd {
return i18n.Tr(l.Lang, format, args...)
}
// in development, we should show an error if a translation key is missing
s, ok := TryTr(l.Lang, format, args...)
if !ok {
log.Error("missing i18n translation key: %q", format)
}
return s
}
// Language specific rules for translating plural texts