2022-08-28 12:43:25 +03:00
// Copyright 2022 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2022-08-28 12:43:25 +03:00
package i18n
import (
"fmt"
"code.gitea.io/gitea/modules/log"
2023-06-02 12:27:30 +03:00
"code.gitea.io/gitea/modules/setting"
2022-08-28 12:43:25 +03:00
)
// 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
2022-09-25 02:00:16 +03:00
func ( store * localeStore ) AddLocaleByIni ( langName , langDesc string , source , moreSource [ ] byte ) error {
2022-08-28 12:43:25 +03:00
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
2023-06-02 12:27:30 +03:00
iniFile , err := setting . NewConfigProviderForLocale ( source , moreSource )
2022-08-28 12:43:25 +03:00
if err != nil {
return fmt . Errorf ( "unable to load ini: %w" , err )
}
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 ( )
}
}
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
}