mirror of
https://github.com/go-gitea/gitea.git
synced 2024-08-20 19:43:27 +02:00
Only en-US has complete translations. When use other language as default, the en-US should still be used as fallback. Close #21199 ### Screenshot ![image](https://user-images.githubusercontent.com/2114189/190882906-b7a83958-0ea2-46c4-9084-42c4f9a239aa.png) Co-authored-by: Lauris BH <lauris@nix.lv>
160 lines
4.0 KiB
Go
160 lines
4.0 KiB
Go
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package i18n
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
// This file implements the static LocaleStore that will not watch for changes
|
|
|
|
type locale struct {
|
|
store *localeStore
|
|
langName string
|
|
idxToMsgMap map[int]string // the map idx is generated by store's trKeyToIdxMap
|
|
}
|
|
|
|
type localeStore struct {
|
|
// After initializing has finished, these fields are read-only.
|
|
langNames []string
|
|
langDescs []string
|
|
|
|
localeMap map[string]*locale
|
|
trKeyToIdxMap map[string]int
|
|
|
|
defaultLang string
|
|
}
|
|
|
|
// NewLocaleStore creates a static locale store
|
|
func NewLocaleStore() LocaleStore {
|
|
return &localeStore{localeMap: make(map[string]*locale), trKeyToIdxMap: make(map[string]int)}
|
|
}
|
|
|
|
// AddLocaleByIni adds locale by ini into the store
|
|
func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error {
|
|
if _, ok := store.localeMap[langName]; ok {
|
|
return ErrLocaleAlreadyExist
|
|
}
|
|
|
|
store.langNames = append(store.langNames, langName)
|
|
store.langDescs = append(store.langDescs, langDesc)
|
|
|
|
l := &locale{store: store, langName: langName, idxToMsgMap: make(map[int]string)}
|
|
store.localeMap[l.langName] = l
|
|
|
|
iniFile, err := ini.LoadSources(ini.LoadOptions{
|
|
IgnoreInlineComment: true,
|
|
UnescapeValueCommentSymbols: true,
|
|
}, source, moreSource)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to load ini: %w", err)
|
|
}
|
|
iniFile.BlockMode = false
|
|
|
|
for _, section := range iniFile.Sections() {
|
|
for _, key := range section.Keys() {
|
|
var trKey string
|
|
if section.Name() == "" || section.Name() == "DEFAULT" {
|
|
trKey = key.Name()
|
|
} else {
|
|
trKey = section.Name() + "." + key.Name()
|
|
}
|
|
idx, ok := store.trKeyToIdxMap[trKey]
|
|
if !ok {
|
|
idx = len(store.trKeyToIdxMap)
|
|
store.trKeyToIdxMap[trKey] = idx
|
|
}
|
|
l.idxToMsgMap[idx] = key.Value()
|
|
}
|
|
}
|
|
iniFile = nil
|
|
|
|
return nil
|
|
}
|
|
|
|
func (store *localeStore) HasLang(langName string) bool {
|
|
_, ok := store.localeMap[langName]
|
|
return ok
|
|
}
|
|
|
|
func (store *localeStore) ListLangNameDesc() (names, desc []string) {
|
|
return store.langNames, store.langDescs
|
|
}
|
|
|
|
// SetDefaultLang sets default language as a fallback
|
|
func (store *localeStore) SetDefaultLang(lang string) {
|
|
store.defaultLang = lang
|
|
}
|
|
|
|
// Tr translates content to target language. fall back to default language.
|
|
func (store *localeStore) Tr(lang, trKey string, trArgs ...interface{}) string {
|
|
l, _ := store.Locale(lang)
|
|
|
|
return l.Tr(trKey, trArgs...)
|
|
}
|
|
|
|
// Has returns whether the given language has a translation for the provided key
|
|
func (store *localeStore) Has(lang, trKey string) bool {
|
|
l, _ := store.Locale(lang)
|
|
|
|
return l.Has(trKey)
|
|
}
|
|
|
|
// Locale returns the locale for the lang or the default language
|
|
func (store *localeStore) Locale(lang string) (Locale, bool) {
|
|
l, found := store.localeMap[lang]
|
|
if !found {
|
|
var ok bool
|
|
l, ok = store.localeMap[store.defaultLang]
|
|
if !ok {
|
|
// no default - return an empty locale
|
|
l = &locale{store: store, idxToMsgMap: make(map[int]string)}
|
|
}
|
|
}
|
|
return l, found
|
|
}
|
|
|
|
// Close implements io.Closer
|
|
func (store *localeStore) Close() error {
|
|
return nil
|
|
}
|
|
|
|
// Tr translates content to locale language. fall back to default language.
|
|
func (l *locale) Tr(trKey string, trArgs ...interface{}) string {
|
|
format := trKey
|
|
|
|
idx, ok := l.store.trKeyToIdxMap[trKey]
|
|
if ok {
|
|
if msg, ok := l.idxToMsgMap[idx]; ok {
|
|
format = msg // use the found translation
|
|
} else if def, ok := l.store.localeMap[l.store.defaultLang]; ok {
|
|
// try to use default locale's translation
|
|
if msg, ok := def.idxToMsgMap[idx]; ok {
|
|
format = msg
|
|
}
|
|
}
|
|
}
|
|
|
|
msg, err := Format(format, trArgs...)
|
|
if err != nil {
|
|
log.Error("Error whilst formatting %q in %s: %v", trKey, l.langName, err)
|
|
}
|
|
return msg
|
|
}
|
|
|
|
// Has returns whether a key is present in this locale or not
|
|
func (l *locale) Has(trKey string) bool {
|
|
idx, ok := l.store.trKeyToIdxMap[trKey]
|
|
if !ok {
|
|
return false
|
|
}
|
|
_, ok = l.idxToMsgMap[idx]
|
|
return ok
|
|
}
|