mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-10-31 23:40:11 +00:00
Restructure language loading to support incomplete translations
On startup, files are scanned and any missing values are replaced with the english version.
This commit is contained in:
parent
1aadd12006
commit
e834445b0b
51
api.go
51
api.go
@ -1085,33 +1085,15 @@ func (app *appContext) GetConfig(gc *gin.Context) {
|
||||
app.info.Println("Config requested")
|
||||
resp := app.configBase
|
||||
// Load language options
|
||||
loadLangs := func(langs *map[string]map[string]interface{}, settingsKey string) (string, []string) {
|
||||
langOptions := make([]string, len(*langs))
|
||||
chosenLang := app.config.Section("ui").Key("language" + settingsKey).MustString("en-us")
|
||||
chosenLangName := (*langs)[chosenLang]["meta"].(map[string]interface{})["name"].(string)
|
||||
i := 0
|
||||
for _, lang := range *langs {
|
||||
langOptions[i] = lang["meta"].(map[string]interface{})["name"].(string)
|
||||
i++
|
||||
}
|
||||
return chosenLangName, langOptions
|
||||
}
|
||||
formChosen, formOptions := loadLangs(&app.storage.lang.Form, "-form")
|
||||
formChosen, formOptions := app.storage.lang.Form.getOptions(app.config.Section("ui").Key("language-form").MustString("en-us"))
|
||||
fl := resp.Sections["ui"].Settings["language-form"]
|
||||
fl.Options = formOptions
|
||||
fl.Value = formChosen
|
||||
adminChosen, adminOptions := loadLangs(&app.storage.lang.Admin, "-admin")
|
||||
adminChosen, adminOptions := app.storage.lang.Admin.getOptions(app.config.Section("ui").Key("language-admin").MustString("en-us"))
|
||||
al := resp.Sections["ui"].Settings["language-admin"]
|
||||
al.Options = adminOptions
|
||||
al.Value = adminChosen
|
||||
emailOptions := make([]string, len(app.storage.lang.Email))
|
||||
chosenLang := app.config.Section("email").Key("language").MustString("en-us")
|
||||
emailChosen := app.storage.lang.Email.get(chosenLang, "meta", "name")
|
||||
i := 0
|
||||
for langName := range app.storage.lang.Email {
|
||||
emailOptions[i] = app.storage.lang.Email.get(langName, "meta", "name")
|
||||
i++
|
||||
}
|
||||
emailChosen, emailOptions := app.storage.lang.Email.getOptions(app.config.Section("email").Key("language").MustString("en-us"))
|
||||
el := resp.Sections["email"].Settings["language"]
|
||||
el.Options = emailOptions
|
||||
el.Value = emailChosen
|
||||
@ -1136,7 +1118,7 @@ func (app *appContext) GetConfig(gc *gin.Context) {
|
||||
|
||||
t := resp.Sections["jellyfin"].Settings["type"]
|
||||
opts := make([]string, len(serverTypes))
|
||||
i = 0
|
||||
i := 0
|
||||
for _, v := range serverTypes {
|
||||
opts[i] = v
|
||||
i++
|
||||
@ -1169,21 +1151,21 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
|
||||
for setting, value := range settings.(map[string]interface{}) {
|
||||
if section == "ui" && setting == "language-form" {
|
||||
for key, lang := range app.storage.lang.Form {
|
||||
if lang["meta"].(map[string]interface{})["name"].(string) == value.(string) {
|
||||
if lang.Meta.Name == value.(string) {
|
||||
tempConfig.Section("ui").Key("language-form").SetValue(key)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if section == "ui" && setting == "language-admin" {
|
||||
for key, lang := range app.storage.lang.Admin {
|
||||
if lang["meta"].(map[string]interface{})["name"].(string) == value.(string) {
|
||||
if lang.Meta.Name == value.(string) {
|
||||
tempConfig.Section("ui").Key("language-admin").SetValue(key)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if section == "email" && setting == "language" {
|
||||
for key := range app.storage.lang.Email {
|
||||
if app.storage.lang.Email.get(key, "meta", "name") == value.(string) {
|
||||
for key, lang := range app.storage.lang.Email {
|
||||
if lang.Meta.Name == value.(string) {
|
||||
tempConfig.Section("email").Key("language").SetValue(key)
|
||||
break
|
||||
}
|
||||
@ -1260,11 +1242,11 @@ func (app *appContext) GetLanguages(gc *gin.Context) {
|
||||
resp := langDTO{}
|
||||
if page == "form" {
|
||||
for key, lang := range app.storage.lang.Form {
|
||||
resp[key] = lang["meta"].(map[string]interface{})["name"].(string)
|
||||
resp[key] = lang.Meta.Name
|
||||
}
|
||||
} else if page == "admin" {
|
||||
for key, lang := range app.storage.lang.Admin {
|
||||
resp[key] = lang["meta"].(map[string]interface{})["name"].(string)
|
||||
resp[key] = lang.Meta.Name
|
||||
}
|
||||
}
|
||||
if len(resp) == 0 {
|
||||
@ -1274,6 +1256,19 @@ func (app *appContext) GetLanguages(gc *gin.Context) {
|
||||
gc.JSON(200, resp)
|
||||
}
|
||||
|
||||
func (app *appContext) ServeLang(gc *gin.Context) {
|
||||
page := gc.Param("page")
|
||||
lang := strings.Replace(gc.Param("file"), ".json", "", 1)
|
||||
if page == "admin" {
|
||||
gc.JSON(200, app.storage.lang.Admin[lang])
|
||||
return
|
||||
} else if page == "form" {
|
||||
gc.JSON(200, app.storage.lang.Form[lang])
|
||||
return
|
||||
}
|
||||
respondBool(400, false, gc)
|
||||
}
|
||||
|
||||
// func Restart() error {
|
||||
// defer func() {
|
||||
// if r := recover(); r != nil {
|
||||
|
65
email.go
65
email.go
@ -73,8 +73,7 @@ func (sm *SMTP) send(address, fromName, fromAddr string, email *Email) error {
|
||||
// Emailer contains the email sender, email content, and methods to construct message content.
|
||||
type Emailer struct {
|
||||
fromAddr, fromName string
|
||||
lang *EmailLang
|
||||
cLang string
|
||||
lang emailLang
|
||||
sender emailClient
|
||||
}
|
||||
|
||||
@ -110,8 +109,7 @@ func NewEmailer(app *appContext) *Emailer {
|
||||
emailer := &Emailer{
|
||||
fromAddr: app.config.Section("email").Key("address").String(),
|
||||
fromName: app.config.Section("email").Key("from").String(),
|
||||
lang: &(app.storage.lang.Email),
|
||||
cLang: app.storage.lang.chosenEmailLang,
|
||||
lang: app.storage.lang.Email[app.storage.lang.chosenEmailLang],
|
||||
}
|
||||
method := app.config.Section("email").Key("method").String()
|
||||
if method == "smtp" {
|
||||
@ -137,7 +135,7 @@ func (emailer *Emailer) NewMailgun(url, key string) {
|
||||
sender := &Mailgun{
|
||||
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
|
||||
}
|
||||
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages'
|
||||
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages')
|
||||
if strings.Contains(url, "messages") {
|
||||
url = url[0:strings.LastIndex(url, "/")]
|
||||
url = url[0:strings.LastIndex(url, "/")]
|
||||
@ -157,9 +155,8 @@ func (emailer *Emailer) NewSMTP(server string, port int, username, password stri
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructInvite(code string, invite Invite, app *appContext) (*Email, error) {
|
||||
lang := emailer.cLang
|
||||
email := &Email{
|
||||
subject: app.config.Section("invite_emails").Key("subject").MustString(emailer.lang.get(lang, "inviteEmail", "title")),
|
||||
subject: app.config.Section("invite_emails").Key("subject").MustString(emailer.lang.InviteEmail.get("title")),
|
||||
}
|
||||
expiry := invite.ValidTill
|
||||
d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern)
|
||||
@ -175,11 +172,11 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"hello": emailer.lang.get(lang, "inviteEmail", "hello"),
|
||||
"youHaveBeenInvited": emailer.lang.get(lang, "inviteEmail", "youHaveBeenInvited"),
|
||||
"toJoin": emailer.lang.get(lang, "inviteEmail", "toJoin"),
|
||||
"inviteExpiry": emailer.lang.format(lang, "inviteEmail", "inviteExpiry", d, t, expiresIn),
|
||||
"linkButton": emailer.lang.get(lang, "inviteEmail", "linkButton"),
|
||||
"hello": emailer.lang.InviteEmail.get("hello"),
|
||||
"youHaveBeenInvited": emailer.lang.InviteEmail.get("youHaveBeenInvited"),
|
||||
"toJoin": emailer.lang.InviteEmail.get("toJoin"),
|
||||
"inviteExpiry": emailer.lang.InviteEmail.format("inviteExpiry", d, t, expiresIn),
|
||||
"linkButton": emailer.lang.InviteEmail.get("linkButton"),
|
||||
"invite_link": inviteLink,
|
||||
"message": message,
|
||||
})
|
||||
@ -196,9 +193,8 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appContext) (*Email, error) {
|
||||
lang := emailer.cLang
|
||||
email := &Email{
|
||||
subject: emailer.lang.get(lang, "inviteExpiry", "title"),
|
||||
subject: emailer.lang.InviteExpiry.get("title"),
|
||||
}
|
||||
expiry := app.formatDatetime(invite.ValidTill)
|
||||
for _, key := range []string{"html", "text"} {
|
||||
@ -209,9 +205,9 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"inviteExpired": emailer.lang.get(lang, "inviteExpiry", "inviteExpired"),
|
||||
"expiredAt": emailer.lang.format(lang, "inviteExpiry", "expiredAt", "\""+code+"\"", expiry),
|
||||
"notificationNotice": emailer.lang.get(lang, "inviteExpiry", "notificationNotice"),
|
||||
"inviteExpired": emailer.lang.InviteExpiry.get("inviteExpired"),
|
||||
"expiredAt": emailer.lang.InviteExpiry.format("expiredAt", "\""+code+"\"", expiry),
|
||||
"notificationNotice": emailer.lang.InviteExpiry.get("notificationNotice"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -226,9 +222,8 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructCreated(code, username, address string, invite Invite, app *appContext) (*Email, error) {
|
||||
lang := emailer.cLang
|
||||
email := &Email{
|
||||
subject: emailer.lang.get(lang, "userCreated", "title"),
|
||||
subject: emailer.lang.UserCreated.get("title"),
|
||||
}
|
||||
created := app.formatDatetime(invite.Created)
|
||||
var tplAddress string
|
||||
@ -245,14 +240,14 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"aUserWasCreated": emailer.lang.format(lang, "userCreated", "aUserWasCreated", "\""+code+"\""),
|
||||
"name": emailer.lang.get(lang, "userCreated", "name"),
|
||||
"address": emailer.lang.get(lang, "userCreated", "emailAddress"),
|
||||
"time": emailer.lang.get(lang, "userCreated", "time"),
|
||||
"aUserWasCreated": emailer.lang.UserCreated.format("aUserWasCreated", "\""+code+"\""),
|
||||
"name": emailer.lang.UserCreated.get("name"),
|
||||
"address": emailer.lang.UserCreated.get("emailAddress"),
|
||||
"time": emailer.lang.UserCreated.get("time"),
|
||||
"nameVal": username,
|
||||
"addressVal": tplAddress,
|
||||
"timeVal": created,
|
||||
"notificationNotice": emailer.lang.get(lang, "userCreated", "notificationNotice"),
|
||||
"notificationNotice": emailer.lang.UserCreated.get("notificationNotice"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -267,9 +262,8 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Email, error) {
|
||||
lang := emailer.cLang
|
||||
email := &Email{
|
||||
subject: emailer.lang.get(lang, "passwordReset", "title"),
|
||||
subject: emailer.lang.PasswordReset.get("title"),
|
||||
}
|
||||
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
||||
message := app.config.Section("email").Key("message").String()
|
||||
@ -281,12 +275,12 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Ema
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"helloUser": emailer.lang.format(lang, "passwordReset", "helloUser", pwr.Username),
|
||||
"someoneHasRequestedReset": emailer.lang.get(lang, "passwordReset", "someoneHasRequestedReset"),
|
||||
"ifItWasYou": emailer.lang.get(lang, "passwordReset", "ifItWasYou"),
|
||||
"codeExpiry": emailer.lang.format(lang, "passwordReset", "codeExpiry", d, t, expiresIn),
|
||||
"ifItWasNotYou": emailer.lang.get(lang, "passwordReset", "ifItWasNotYou"),
|
||||
"pin": emailer.lang.get(lang, "passwordReset", "pin"),
|
||||
"helloUser": emailer.lang.PasswordReset.format("helloUser", pwr.Username),
|
||||
"someoneHasRequestedReset": emailer.lang.PasswordReset.get("someoneHasRequestedReset"),
|
||||
"ifItWasYou": emailer.lang.PasswordReset.get("ifItWasYou"),
|
||||
"codeExpiry": emailer.lang.PasswordReset.format("codeExpiry", d, t, expiresIn),
|
||||
"ifItWasNotYou": emailer.lang.PasswordReset.get("ifItWasNotYou"),
|
||||
"pin": emailer.lang.PasswordReset.get("pin"),
|
||||
"pinVal": pwr.Pin,
|
||||
"message": message,
|
||||
})
|
||||
@ -303,9 +297,8 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Ema
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email, error) {
|
||||
lang := emailer.cLang
|
||||
email := &Email{
|
||||
subject: emailer.lang.get(lang, "userDeleted", "title"),
|
||||
subject: emailer.lang.UserDeleted.get("title"),
|
||||
}
|
||||
for _, key := range []string{"html", "text"} {
|
||||
fpath := app.config.Section("deletion").Key("email_" + key).String()
|
||||
@ -315,8 +308,8 @@ func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"yourAccountWasDeleted": emailer.lang.get(lang, "userDeleted", "yourAccountWasDeleted"),
|
||||
"reason": emailer.lang.get(lang, "userDeleted", "reason"),
|
||||
"yourAccountWasDeleted": emailer.lang.UserDeleted.get("yourAccountWasDeleted"),
|
||||
"reason": emailer.lang.UserDeleted.get("reason"),
|
||||
"reasonVal": reason,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -253,9 +253,9 @@
|
||||
<div class="card ~neutral !low accounts mb-1">
|
||||
<span class="heading">{{ .strings.accounts }}</span>
|
||||
<div class="fr">
|
||||
<span class="button ~neutral !normal" id="accounts-add-user">{{ .quantityStrings.addUser.singular }}</span>
|
||||
<span class="button ~neutral !normal" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
|
||||
<span class="button ~urge !normal" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
|
||||
<span class="button ~critical !normal" id="accounts-delete-user">{{ .quantityStrings.deleteUser.singular }}</span>
|
||||
<span class="button ~critical !normal" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
|
||||
</div>
|
||||
<div class="card ~neutral !normal accounts-header table-responsive mt-half">
|
||||
<table class="table">
|
||||
|
74
lang.go
74
lang.go
@ -1,5 +1,7 @@
|
||||
package main
|
||||
|
||||
import "strings"
|
||||
|
||||
type langMeta struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
@ -9,14 +11,80 @@ type quantityString struct {
|
||||
Plural string `json:"plural"`
|
||||
}
|
||||
|
||||
type adminLangs map[string]adminLang
|
||||
|
||||
func (ls *adminLangs) getOptions(chosen string) (string, []string) {
|
||||
opts := make([]string, len(*ls))
|
||||
chosenLang := (*ls)[chosen].Meta.Name
|
||||
i := 0
|
||||
for _, lang := range *ls {
|
||||
opts[i] = lang.Meta.Name
|
||||
}
|
||||
return chosenLang, opts
|
||||
}
|
||||
|
||||
type adminLang struct {
|
||||
Meta langMeta `json:"meta"`
|
||||
Strings map[string]string `json:"strings"`
|
||||
Notifications map[string]string `json:"notifications"`
|
||||
Strings langSection `json:"strings"`
|
||||
Notifications langSection `json:"notifications"`
|
||||
QuantityStrings map[string]quantityString `json:"quantityStrings"`
|
||||
JSON string
|
||||
}
|
||||
|
||||
type formLangs map[string]formLang
|
||||
|
||||
func (ls *formLangs) getOptions(chosen string) (string, []string) {
|
||||
opts := make([]string, len(*ls))
|
||||
chosenLang := (*ls)[chosen].Meta.Name
|
||||
i := 0
|
||||
for _, lang := range *ls {
|
||||
opts[i] = lang.Meta.Name
|
||||
}
|
||||
return chosenLang, opts
|
||||
}
|
||||
|
||||
type formLang struct {
|
||||
Strings map[string]string `json:"strings"`
|
||||
Meta langMeta `json:"meta"`
|
||||
Strings langSection `json:"strings"`
|
||||
ValidationStrings map[string]quantityString `json:"validationStrings"`
|
||||
validationStringsJSON string
|
||||
}
|
||||
|
||||
type emailLangs map[string]emailLang
|
||||
|
||||
func (ls *emailLangs) getOptions(chosen string) (string, []string) {
|
||||
opts := make([]string, len(*ls))
|
||||
chosenLang := (*ls)[chosen].Meta.Name
|
||||
i := 0
|
||||
for _, lang := range *ls {
|
||||
opts[i] = lang.Meta.Name
|
||||
}
|
||||
return chosenLang, opts
|
||||
}
|
||||
|
||||
type emailLang struct {
|
||||
Meta langMeta `json:"meta"`
|
||||
UserCreated langSection `json:"userCreated"`
|
||||
InviteExpiry langSection `json:"inviteExpiry"`
|
||||
PasswordReset langSection `json:"passwordReset"`
|
||||
UserDeleted langSection `json:"userDeleted"`
|
||||
InviteEmail langSection `json:"inviteEmail"`
|
||||
}
|
||||
|
||||
type langSection map[string]string
|
||||
|
||||
func (el *langSection) format(field string, vals ...string) string {
|
||||
text := el.get(field)
|
||||
for _, val := range vals {
|
||||
text = strings.Replace(text, "{n}", val, 1)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func (el *langSection) get(field string) string {
|
||||
t, ok := (*el)[field]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
3
main.go
3
main.go
@ -569,7 +569,6 @@ func start(asDaemon, firstCall bool) {
|
||||
|
||||
router.Use(gin.Recovery())
|
||||
router.Use(static.Serve("/", static.LocalFile(filepath.Join(app.localPath, "web"), false)))
|
||||
router.Use(static.Serve("/lang/", static.LocalFile(filepath.Join(app.localPath, "lang"), false)))
|
||||
app.loadHTML(router)
|
||||
router.NoRoute(app.NoRouteHandler)
|
||||
if debugMode {
|
||||
@ -580,7 +579,7 @@ func start(asDaemon, firstCall bool) {
|
||||
router.GET("/", app.AdminPage)
|
||||
router.GET("/accounts", app.AdminPage)
|
||||
router.GET("/settings", app.AdminPage)
|
||||
|
||||
router.GET("/lang/:page/:file", app.ServeLang)
|
||||
router.GET("/lang/:page", app.GetLanguages)
|
||||
router.GET("/token/login", app.getTokenLogin)
|
||||
router.GET("/token/refresh", app.getTokenRefresh)
|
||||
|
364
storage.go
364
storage.go
@ -20,36 +20,6 @@ type Storage struct {
|
||||
lang Lang
|
||||
}
|
||||
|
||||
type EmailLang map[string]map[string]map[string]interface{} // Map of lang codes to email name to fields
|
||||
|
||||
func (el *EmailLang) format(lang, email, field string, vals ...string) string {
|
||||
text := el.get(lang, email, field)
|
||||
for _, val := range vals {
|
||||
text = strings.Replace(text, "{n}", val, 1)
|
||||
}
|
||||
return text
|
||||
}
|
||||
func (el *EmailLang) get(lang, email, field string) string {
|
||||
t, ok := (*el)[lang][email][field]
|
||||
if !ok {
|
||||
t = (*el)["en-us"][email][field]
|
||||
}
|
||||
return t.(string)
|
||||
}
|
||||
|
||||
type Lang struct {
|
||||
chosenFormLang string
|
||||
chosenAdminLang string
|
||||
chosenEmailLang string
|
||||
AdminPath string
|
||||
Admin map[string]map[string]interface{}
|
||||
AdminJSON map[string]string
|
||||
FormPath string
|
||||
Form map[string]map[string]interface{}
|
||||
EmailPath string
|
||||
Email EmailLang
|
||||
}
|
||||
|
||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||
|
||||
type Profile struct {
|
||||
@ -73,6 +43,202 @@ type Invite struct {
|
||||
Profile string `json:"profile"`
|
||||
}
|
||||
|
||||
type Lang struct {
|
||||
chosenFormLang string
|
||||
chosenAdminLang string
|
||||
chosenEmailLang string
|
||||
AdminPath string
|
||||
Admin adminLangs
|
||||
AdminJSON map[string]string
|
||||
FormPath string
|
||||
Form formLangs
|
||||
EmailPath string
|
||||
Email emailLangs
|
||||
}
|
||||
|
||||
func (st *Storage) loadLang() (err error) {
|
||||
err = st.loadLangAdmin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = st.loadLangForm()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = st.loadLangEmail()
|
||||
return
|
||||
}
|
||||
|
||||
// If a given language has missing values, fill it in with the english value.
|
||||
func patchLang(english, other *langSection) {
|
||||
for n, ev := range *english {
|
||||
if v, ok := (*other)[n]; !ok || v == "" {
|
||||
(*other)[n] = ev
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func patchQuantityStrings(english, other *map[string]quantityString) {
|
||||
for n, ev := range *english {
|
||||
qs, ok := (*other)[n]
|
||||
if !ok {
|
||||
(*other)[n] = ev
|
||||
return
|
||||
} else if qs.Singular == "" {
|
||||
qs.Singular = ev.Singular
|
||||
} else if (*other)[n].Plural == "" {
|
||||
qs.Plural = ev.Plural
|
||||
}
|
||||
(*other)[n] = qs
|
||||
}
|
||||
}
|
||||
|
||||
func (st *Storage) loadLangAdmin() error {
|
||||
st.lang.Admin = map[string]adminLang{}
|
||||
var english adminLang
|
||||
load := func(fname string) error {
|
||||
index := strings.TrimSuffix(fname, filepath.Ext(fname))
|
||||
lang := adminLang{}
|
||||
f, err := ioutil.ReadFile(filepath.Join(st.lang.AdminPath, fname))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if substituteStrings != "" {
|
||||
f = []byte(strings.ReplaceAll(string(f), "Jellyfin", substituteStrings))
|
||||
}
|
||||
err = json.Unmarshal(f, &lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fname != "en-us.json" {
|
||||
patchLang(&english.Strings, &lang.Strings)
|
||||
patchLang(&english.Notifications, &lang.Notifications)
|
||||
patchQuantityStrings(&english.QuantityStrings, &lang.QuantityStrings)
|
||||
}
|
||||
stringAdmin, err := json.Marshal(lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lang.JSON = string(stringAdmin)
|
||||
st.lang.Admin[index] = lang
|
||||
return nil
|
||||
}
|
||||
err := load("en-us.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
english = st.lang.Admin["en-us"]
|
||||
files, err := ioutil.ReadDir(st.lang.AdminPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range files {
|
||||
if f.Name() != "en-us.json" {
|
||||
err = load(f.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *Storage) loadLangForm() error {
|
||||
st.lang.Form = map[string]formLang{}
|
||||
var english formLang
|
||||
load := func(fname string) error {
|
||||
index := strings.TrimSuffix(fname, filepath.Ext(fname))
|
||||
lang := formLang{}
|
||||
f, err := ioutil.ReadFile(filepath.Join(st.lang.FormPath, fname))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if substituteStrings != "" {
|
||||
f = []byte(strings.ReplaceAll(string(f), "Jellyfin", substituteStrings))
|
||||
}
|
||||
err = json.Unmarshal(f, &lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fname != "en-us.json" {
|
||||
patchLang(&english.Strings, &lang.Strings)
|
||||
patchQuantityStrings(&english.ValidationStrings, &lang.ValidationStrings)
|
||||
}
|
||||
validationStrings, err := json.Marshal(lang.ValidationStrings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lang.validationStringsJSON = string(validationStrings)
|
||||
st.lang.Form[index] = lang
|
||||
return nil
|
||||
}
|
||||
err := load("en-us.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
english = st.lang.Form["en-us"]
|
||||
files, err := ioutil.ReadDir(st.lang.FormPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range files {
|
||||
if f.Name() != "en-us.json" {
|
||||
err = load(f.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *Storage) loadLangEmail() error {
|
||||
st.lang.Email = map[string]emailLang{}
|
||||
var english emailLang
|
||||
load := func(fname string) error {
|
||||
index := strings.TrimSuffix(fname, filepath.Ext(fname))
|
||||
lang := emailLang{}
|
||||
f, err := ioutil.ReadFile(filepath.Join(st.lang.EmailPath, fname))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if substituteStrings != "" {
|
||||
f = []byte(strings.ReplaceAll(string(f), "Jellyfin", substituteStrings))
|
||||
}
|
||||
err = json.Unmarshal(f, &lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fname != "en-us.json" {
|
||||
patchLang(&english.UserCreated, &lang.UserCreated)
|
||||
patchLang(&english.InviteExpiry, &lang.InviteExpiry)
|
||||
patchLang(&english.PasswordReset, &lang.PasswordReset)
|
||||
patchLang(&english.UserDeleted, &lang.UserDeleted)
|
||||
patchLang(&english.InviteEmail, &lang.InviteEmail)
|
||||
}
|
||||
st.lang.Email[index] = lang
|
||||
return nil
|
||||
}
|
||||
err := load("en-us.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
english = st.lang.Email["en-us"]
|
||||
files, err := ioutil.ReadDir(st.lang.EmailPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range files {
|
||||
if f.Name() != "en-us.json" {
|
||||
err = load(f.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Invites map[string]Invite
|
||||
|
||||
func (st *Storage) loadInvites() error {
|
||||
@ -83,75 +249,75 @@ func (st *Storage) storeInvites() error {
|
||||
return storeJSON(st.invite_path, st.invites)
|
||||
}
|
||||
|
||||
func (st *Storage) loadLang() error {
|
||||
loadData := func(path string, stringJson bool) (map[string]string, map[string]map[string]interface{}, error) {
|
||||
files, err := ioutil.ReadDir(path)
|
||||
outString := map[string]string{}
|
||||
out := map[string]map[string]interface{}{}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, f := range files {
|
||||
index := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
||||
var data map[string]interface{}
|
||||
var file []byte
|
||||
var err error
|
||||
file, err = ioutil.ReadFile(filepath.Join(path, f.Name()))
|
||||
if err != nil {
|
||||
file = []byte("{}")
|
||||
}
|
||||
// Replace Jellyfin with something if necessary
|
||||
if substituteStrings != "" {
|
||||
fileString := strings.ReplaceAll(string(file), "Jellyfin", substituteStrings)
|
||||
file = []byte(fileString)
|
||||
}
|
||||
err = json.Unmarshal(file, &data)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Failed to read \"%s\": %s", path, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
if stringJson {
|
||||
stringJSON, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
outString[index] = string(stringJSON)
|
||||
}
|
||||
out[index] = data
|
||||
|
||||
}
|
||||
return outString, out, nil
|
||||
}
|
||||
_, form, err := loadData(st.lang.FormPath, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for index, lang := range form {
|
||||
validationStrings := lang["validationStrings"].(map[string]interface{})
|
||||
vS, err := json.Marshal(validationStrings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lang["validationStrings"] = string(vS)
|
||||
form[index] = lang
|
||||
}
|
||||
st.lang.Form = form
|
||||
adminJSON, admin, err := loadData(st.lang.AdminPath, true)
|
||||
st.lang.Admin = admin
|
||||
st.lang.AdminJSON = adminJSON
|
||||
|
||||
_, emails, err := loadData(st.lang.EmailPath, false)
|
||||
fixedEmails := map[string]map[string]map[string]interface{}{}
|
||||
for lang, e := range emails {
|
||||
f := map[string]map[string]interface{}{}
|
||||
for field, vals := range e {
|
||||
f[field] = vals.(map[string]interface{})
|
||||
}
|
||||
fixedEmails[lang] = f
|
||||
}
|
||||
st.lang.Email = fixedEmails
|
||||
return err
|
||||
}
|
||||
// func (st *Storage) loadLang() error {
|
||||
// loadData := func(path string, stringJson bool) (map[string]string, map[string]map[string]interface{}, error) {
|
||||
// files, err := ioutil.ReadDir(path)
|
||||
// outString := map[string]string{}
|
||||
// out := map[string]map[string]interface{}{}
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// for _, f := range files {
|
||||
// index := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
||||
// var data map[string]interface{}
|
||||
// var file []byte
|
||||
// var err error
|
||||
// file, err = ioutil.ReadFile(filepath.Join(path, f.Name()))
|
||||
// if err != nil {
|
||||
// file = []byte("{}")
|
||||
// }
|
||||
// // Replace Jellyfin with something if necessary
|
||||
// if substituteStrings != "" {
|
||||
// fileString := strings.ReplaceAll(string(file), "Jellyfin", substituteStrings)
|
||||
// file = []byte(fileString)
|
||||
// }
|
||||
// err = json.Unmarshal(file, &data)
|
||||
// if err != nil {
|
||||
// log.Printf("ERROR: Failed to read \"%s\": %s", path, err)
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// if stringJson {
|
||||
// stringJSON, err := json.Marshal(data)
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// outString[index] = string(stringJSON)
|
||||
// }
|
||||
// out[index] = data
|
||||
//
|
||||
// }
|
||||
// return outString, out, nil
|
||||
// }
|
||||
// _, form, err := loadData(st.lang.FormPath, false)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// for index, lang := range form {
|
||||
// validationStrings := lang["validationStrings"].(map[string]interface{})
|
||||
// vS, err := json.Marshal(validationStrings)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// lang["validationStrings"] = string(vS)
|
||||
// form[index] = lang
|
||||
// }
|
||||
// st.lang.Form = form
|
||||
// adminJSON, admin, err := loadData(st.lang.AdminPath, true)
|
||||
// st.lang.Admin = admin
|
||||
// st.lang.AdminJSON = adminJSON
|
||||
//
|
||||
// _, emails, err := loadData(st.lang.EmailPath, false)
|
||||
// fixedEmails := map[string]map[string]map[string]interface{}{}
|
||||
// for lang, e := range emails {
|
||||
// f := map[string]map[string]interface{}{}
|
||||
// for field, vals := range e {
|
||||
// f[field] = vals.(map[string]interface{})
|
||||
// }
|
||||
// fixedEmails[lang] = f
|
||||
// }
|
||||
// st.lang.Email = fixedEmails
|
||||
// return err
|
||||
// }
|
||||
|
||||
func (st *Storage) loadEmails() error {
|
||||
return loadJSON(st.emails_path, &st.emails)
|
||||
|
12
views.go
12
views.go
@ -45,7 +45,7 @@ func (app *appContext) AdminPage(gc *gin.Context) {
|
||||
lang := gc.Query("lang")
|
||||
if lang == "" {
|
||||
lang = app.storage.lang.chosenAdminLang
|
||||
} else if _, ok := app.storage.lang.Form[lang]; !ok {
|
||||
} else if _, ok := app.storage.lang.Admin[lang]; !ok {
|
||||
lang = app.storage.lang.chosenAdminLang
|
||||
}
|
||||
emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool()
|
||||
@ -61,9 +61,9 @@ func (app *appContext) AdminPage(gc *gin.Context) {
|
||||
"commit": COMMIT,
|
||||
"ombiEnabled": ombiEnabled,
|
||||
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
||||
"strings": app.storage.lang.Admin[lang]["strings"],
|
||||
"quantityStrings": app.storage.lang.Admin[lang]["quantityStrings"],
|
||||
"language": app.storage.lang.AdminJSON[lang],
|
||||
"strings": app.storage.lang.Admin[lang].Strings,
|
||||
"quantityStrings": app.storage.lang.Admin[lang].QuantityStrings,
|
||||
"language": app.storage.lang.Admin[lang].JSON,
|
||||
})
|
||||
}
|
||||
|
||||
@ -94,8 +94,8 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
|
||||
"requirements": app.validator.getCriteria(),
|
||||
"email": email,
|
||||
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
||||
"strings": app.storage.lang.Form[lang]["strings"],
|
||||
"validationStrings": app.storage.lang.Form[lang]["validationStrings"],
|
||||
"strings": app.storage.lang.Form[lang].Strings,
|
||||
"validationStrings": app.storage.lang.Form[lang].validationStringsJSON,
|
||||
"code": code,
|
||||
})
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user