mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 17:10:10 +00:00
Add email translation, add part of french translations
Admin translation from @Killianbe, Email translation from @Cornichon420. French is currently not functional, a few things are missing which i'm waiting on.
This commit is contained in:
parent
1707d011a2
commit
0710e05479
4
api.go
4
api.go
@ -455,7 +455,7 @@ func (app *appContext) DeleteUser(gc *gin.Context) {
|
||||
app.err.Printf("%s: Failed to send to %s", userID, address)
|
||||
app.debug.Printf("%s: Error: %s", userID, err)
|
||||
} else {
|
||||
app.info.Printf("%s: Sent invite email to %s", userID, address)
|
||||
app.info.Printf("%s: Sent deletion email to %s", userID, address)
|
||||
}
|
||||
}(userID, req.Reason, addr.(string))
|
||||
}
|
||||
@ -1176,7 +1176,7 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if value.(string) != app.config.Section(section).Key(setting).MustString("") {
|
||||
tempConfig.Section(section).Key(setting).SetValue(value.(string))
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func (app *appContext) loadConfig() error {
|
||||
key.SetValue(key.MustString(filepath.Join(app.dataPath, (key.Name() + ".json"))))
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template"} {
|
||||
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template"} {
|
||||
// if app.config.Section("files").Key(key).MustString("") == "" {
|
||||
// key.SetValue(filepath.Join(app.data_path, (key.Name() + ".json")))
|
||||
// }
|
||||
@ -80,8 +80,6 @@ func (app *appContext) loadConfig() error {
|
||||
app.config.Section("jellyfin").Key("device").SetValue("jfa-go")
|
||||
app.config.Section("jellyfin").Key("device_id").SetValue(fmt.Sprintf("jfa-go-%s-%s", VERSION, COMMIT))
|
||||
|
||||
app.email = NewEmailer(app)
|
||||
|
||||
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
||||
|
||||
oldFormLang := app.config.Section("ui").Key("language").MustString("")
|
||||
@ -93,6 +91,9 @@ func (app *appContext) loadConfig() error {
|
||||
app.storage.lang.chosenFormLang = newFormLang
|
||||
}
|
||||
app.storage.lang.chosenAdminLang = app.config.Section("ui").Key("language-admin").MustString("en-us")
|
||||
app.storage.lang.chosenEmailLang = app.config.Section("email").Key("language").MustString("en-us")
|
||||
|
||||
app.email = NewEmailer(app)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -277,6 +277,18 @@
|
||||
"description": "General email settings. Ignore if not using email features."
|
||||
},
|
||||
"settings": {
|
||||
"language": {
|
||||
"name": "Email Language",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "method",
|
||||
"type": "select",
|
||||
"options": [
|
||||
"en-us"
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default email language. Submit a PR on github if you'd like to translate."
|
||||
},
|
||||
"no_username": {
|
||||
"name": "Use email addresses as username",
|
||||
"required": false,
|
||||
|
60
email.go
60
email.go
@ -73,6 +73,8 @@ 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
|
||||
sender emailClient
|
||||
}
|
||||
|
||||
@ -108,6 +110,8 @@ 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,
|
||||
}
|
||||
method := app.config.Section("email").Key("method").String()
|
||||
if method == "smtp" {
|
||||
@ -153,8 +157,9 @@ 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").String(),
|
||||
subject: app.config.Section("invite_emails").Key("subject").MustString(emailer.lang.get(lang, "inviteEmail", "title")),
|
||||
}
|
||||
expiry := invite.ValidTill
|
||||
d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern)
|
||||
@ -170,9 +175,11 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"expiry_date": d,
|
||||
"expiry_time": t,
|
||||
"expires_in": expiresIn,
|
||||
"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"),
|
||||
"invite_link": inviteLink,
|
||||
"message": message,
|
||||
})
|
||||
@ -189,8 +196,9 @@ 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: "Notice: Invite expired",
|
||||
subject: emailer.lang.get(lang, "inviteExpiry", "title"),
|
||||
}
|
||||
expiry := app.formatDatetime(invite.ValidTill)
|
||||
for _, key := range []string{"html", "text"} {
|
||||
@ -201,8 +209,9 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"code": code,
|
||||
"expiry": expiry,
|
||||
"inviteExpired": emailer.lang.get(lang, "inviteExpiry", "inviteExpired"),
|
||||
"expiredAt": emailer.lang.format(lang, "inviteExpiry", "expiredAt", "\""+code+"\"", expiry),
|
||||
"notificationNotice": emailer.lang.get(lang, "inviteExpiry", "notificationNotice"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -217,8 +226,9 @@ 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: "Notice: User created",
|
||||
subject: emailer.lang.get(lang, "userCreated", "title"),
|
||||
}
|
||||
created := app.formatDatetime(invite.Created)
|
||||
var tplAddress string
|
||||
@ -235,10 +245,14 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"code": code,
|
||||
"username": username,
|
||||
"address": tplAddress,
|
||||
"time": created,
|
||||
"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"),
|
||||
"nameVal": username,
|
||||
"addressVal": tplAddress,
|
||||
"timeVal": created,
|
||||
"notificationNotice": emailer.lang.get(lang, "userCreated", "notificationNotice"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -253,8 +267,9 @@ 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: app.config.Section("password_resets").Key("subject").MustString("Password reset - Jellyfin"),
|
||||
subject: emailer.lang.get(lang, "passwordReset", "title"),
|
||||
}
|
||||
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
||||
message := app.config.Section("email").Key("message").String()
|
||||
@ -266,11 +281,13 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Ema
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"username": pwr.Username,
|
||||
"expiry_date": d,
|
||||
"expiry_time": t,
|
||||
"expires_in": expiresIn,
|
||||
"pin": pwr.Pin,
|
||||
"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"),
|
||||
"pinVal": pwr.Pin,
|
||||
"message": message,
|
||||
})
|
||||
if err != nil {
|
||||
@ -286,8 +303,9 @@ 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: app.config.Section("deletion").Key("subject").MustString("Your account was deleted - Jellyfin"),
|
||||
subject: emailer.lang.get(lang, "userDeleted", "title"),
|
||||
}
|
||||
for _, key := range []string{"html", "text"} {
|
||||
fpath := app.config.Section("deletion").Key("email_" + key).String()
|
||||
@ -297,7 +315,9 @@ func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, map[string]string{
|
||||
"reason": reason,
|
||||
"yourAccountWasDeleted": emailer.lang.get(lang, "userDeleted", "yourAccountWasDeleted"),
|
||||
"reason": emailer.lang.get(lang, "userDeleted", "reason"),
|
||||
"reasonVal": reason,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
78
lang/admin/fr-fr.json
Normal file
78
lang/admin/fr-fr.json
Normal file
@ -0,0 +1,78 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Francais (FR)",
|
||||
"author": "https://github.com/Killianbe"
|
||||
},
|
||||
"strings": {
|
||||
"invites": "Invite",
|
||||
"accounts": "Comptes",
|
||||
"settings": "Reglages",
|
||||
"theme": "Thème",
|
||||
"inviteDays": "Jours",
|
||||
"inviteHours": "Heures",
|
||||
"inviteMinutes": "Minutes",
|
||||
"inviteNumberOfUses": "Nombre d'utilisateur",
|
||||
"warning": "Attention",
|
||||
"inviteInfiniteUsesWarning": "les invitations infinies peuvent être utilisées abusivement",
|
||||
"inviteSendToEmail": "Envoyer à",
|
||||
"login": "S'identifier",
|
||||
"logout": "Se déconecter",
|
||||
"create": "Créer",
|
||||
"apply": "Appliquer",
|
||||
"delete": "Effacer",
|
||||
"submit": "Soumettre",
|
||||
"name": "Nom",
|
||||
"username": "Nom d'utilisateur",
|
||||
"password": "Mot de passe",
|
||||
"emailAddress": "Addresse Email",
|
||||
"lastActiveTime": "Dernière activité",
|
||||
"from": "De",
|
||||
"user": "Utilisateur",
|
||||
"aboutProgram": "A propros",
|
||||
"version": "Version",
|
||||
"commitNoun": "Commettre",
|
||||
"newUser": "Nouvel utilisateur",
|
||||
"profile": "Profil",
|
||||
"modifySettings": "Modifier les paramètres",
|
||||
"modifySettingsDescription": "Appliquez les paramètres à partir d'un profil existant ou obtenez-les directement auprès d'un utilisateur.",
|
||||
"applyHomescreenLayout": "Appliquer la disposition de l'écran d'accueil",
|
||||
"sendDeleteNotificationEmail": "Envoyer un e-mail de notification ",
|
||||
"sendDeleteNotifiationExample": "Votre compte a été supprimé. ",
|
||||
"settingsRestartRequired": "Redémarrage nécessaire ",
|
||||
"settingsRestartRequiredDescription": "Un redémarrage est nécessaire pour appliquer certains paramètres que vous avez modifiés. Redémarrer maintenant ou plus tard?",
|
||||
"settingsApplyRestartLater": "Appliquer, redémarrer plus tard ",
|
||||
"settingsApplyRestartNow": "Appliquer et redémarrer ",
|
||||
"settingsApplied": "Paramètres appliqués.",
|
||||
"settingsRefreshPage": "Actualisez la page dans quelques secondes ",
|
||||
"settingsRequiredOrRestartMessage": "Remarque: {n} indique un champ obligatoire, {n} indique que les modifications nécessitent un redémarrage. ",
|
||||
"settingsSave": "Sauver",
|
||||
"ombiUserDefaults": "Paramètres par défaut de l'utilisateur Ombi",
|
||||
"ombiUserDefaultsDescription": "Créez un utilisateur Ombi et configurez-le, puis sélectionnez-le ci-dessous. Ses paramètres / autorisations seront stockés et appliqués aux nouveaux utilisateurs Ombi créés par jfa-go ",
|
||||
"userProfiles": "Profils d'utilisateurs",
|
||||
"userProfilesDescription": "Les profils sont appliqués aux utilisateurs lorsqu'ils créent un compte. Un profil inclut les droits d'accès à la bibliothèque et la disposition de l'écran d'accueil. ",
|
||||
"userProfilesIsDefault": "Défaut",
|
||||
"userProfilesLibraries": "Bibliothèques",
|
||||
"addProfile": "Ajouter un profil",
|
||||
"addProfileDescription": "Créez un utilisateur Jellyfin et configurez-le, puis sélectionnez-le ci-dessous. Lorsque ce profil est appliqué à une invitation, de nouveaux utilisateurs seront créés avec les paramètres. ",
|
||||
"addProfileNameOf": "Nom de profil",
|
||||
"addProfileStoreHomescreenLayout": "Enregistrer la disposition de l'écran d'accueil"
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
"singular": "Modifier les paramètres pour {n} utilisateur",
|
||||
"plural": "Modifier les paramètres pour {n} utilisateurs"
|
||||
},
|
||||
"deleteNUsers": {
|
||||
"singular": "Supprimer {n} utilisateur",
|
||||
"plural": "Supprimer {n} utilisateurs"
|
||||
},
|
||||
"addUser": {
|
||||
"singular": "Ajouter un utilisateur",
|
||||
"plural": "Ajouter des utilisateurs"
|
||||
},
|
||||
"deleteUser": {
|
||||
"singular": "Supprimer l'utilisateur",
|
||||
"plural": "Supprimer les utilisateurs"
|
||||
}
|
||||
}
|
||||
}
|
41
lang/email/en-us.json
Normal file
41
lang/email/en-us.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "English (US)"
|
||||
},
|
||||
"userCreated": {
|
||||
"title": "Notice: User created",
|
||||
"aUserWasCreated": "A user was created using code {n}.",
|
||||
"name": "Name",
|
||||
"emailAddress": "Address",
|
||||
"time": "Time",
|
||||
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
||||
},
|
||||
"inviteExpiry": {
|
||||
"title": "Notice: Invite expired",
|
||||
"inviteExpired": "Invite expired.",
|
||||
"expiredAt": "Code {n} expired at {n}.",
|
||||
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
||||
},
|
||||
"passwordReset": {
|
||||
"title": "Password reset requested - Jellyfin",
|
||||
"helloUser": "Hi {n},",
|
||||
"someoneHasRequestedReset": "Someone has recently requested a password reset on Jellyfin.",
|
||||
"ifItWasYou": "If this was you, enter the pin below into the prompt.",
|
||||
"codeExpiry": "The code will expire on {n}, at {n} UTC, which is in {n}.",
|
||||
"ifItWasNotYou": "If this wasn't you, please ignore this email.",
|
||||
"pin": "PIN"
|
||||
},
|
||||
"userDeleted": {
|
||||
"title": "Your account was deleted - Jellyfin",
|
||||
"yourAccountWasDeleted": "Your Jellyfin account was deleted.",
|
||||
"reason": "Reason"
|
||||
},
|
||||
"inviteEmail": {
|
||||
"title": "Invite - Jellyfin",
|
||||
"hello": "Hi",
|
||||
"youHaveBeenInvited": "You've been invited to Jellyfin.",
|
||||
"toJoin": "To join, follow the below link.",
|
||||
"inviteExpiry": "This invite will expire on {n}, at {n}, which is in {n}, so act quick.",
|
||||
"linkButton": "Setup your account"
|
||||
}
|
||||
}
|
32
lang/email/fr-fr.json
Normal file
32
lang/email/fr-fr.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Francais (FR)",
|
||||
"author": "https://github.com/Cornichon420"
|
||||
},
|
||||
"userCreated": {
|
||||
"aUserWasCreated": "Un utilisateur a été créé avec ce code {n}",
|
||||
"name": "Nom",
|
||||
"emailAddress": "Adresse",
|
||||
"time": "Date",
|
||||
"notificationNotice": ""
|
||||
},
|
||||
"passwordReset": {
|
||||
"helloUser": "Salut {n},",
|
||||
"someoneHasRequestedReset": "Quelqu'un vient de demander une réinitialisation du mot de passe via Jellyfin.",
|
||||
"ifItWasYou": "Si c'était bien toi, renseigne le code PIN en dessous.",
|
||||
"codeExpiry": "Ce code expirera le {n}, à {n} UTC, soit dans {n}.",
|
||||
"ifItWasNotYou": "Si ce n'était pas toi, tu peux ignorer ce mail.",
|
||||
"pin": "PIN"
|
||||
},
|
||||
"userDeleted": {
|
||||
"yourAccountWasDeleted": "Ton compte Jellyfin a été supprimé.",
|
||||
"reason": "Motif"
|
||||
},
|
||||
"inviteEmail": {
|
||||
"hello": "Salut",
|
||||
"youHaveBeenInvited": "Tu a été invité à rejoindre Jellyfin.",
|
||||
"toJoin": "Pour continuer, suis le lien en dessous.",
|
||||
"inviteExpiry": "L'invitation expirera le {n}, à {n}, sout dans {n}, alors fais vite !",
|
||||
"linkButton": "Lien"
|
||||
}
|
||||
}
|
@ -20,26 +20,25 @@
|
||||
<mj-section mj-class="bg">
|
||||
<mj-column>
|
||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||
<h3>User Created</h3>
|
||||
<p>A user was created using code {{ .code }}.</p>
|
||||
<p>{{ .aUserWasCreated }}</p>
|
||||
</mj-text>
|
||||
<mj-table mj-class="text" container-background-color="#242424">
|
||||
<tr style="text-align: left;">
|
||||
<th>Name</th>
|
||||
<th>Address</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
<tr style="font-style: italic; text-align: left; color: rgb(153,153,153);">
|
||||
<th>{{ .username }}</th>
|
||||
<th>{{ .name }}</th>
|
||||
<th>{{ .address }}</th>
|
||||
<th>{{ .time }}</th>
|
||||
</tr>
|
||||
<tr style="font-style: italic; text-align: left; color: rgb(153,153,153);">
|
||||
<th>{{ .nameVal }}</th>
|
||||
<th>{{ .addressVal }}</th>
|
||||
<th>{{ .timeVal }}</th>
|
||||
</mj-table>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section mj-class="bg2">
|
||||
<mj-column>
|
||||
<mj-text mj-class="secondary" font-style="italic" font-size="14px">
|
||||
Notification emails can be toggled on the admin dashboard.
|
||||
{{ .notificationNotice }}
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
@ -1,7 +1,7 @@
|
||||
A user was created using code {{ .code }}.
|
||||
{{ .aUserWasCreated }}
|
||||
|
||||
Name: {{ .username }}
|
||||
Address: {{ .address }}
|
||||
Time: {{ .time }}
|
||||
{{ .name }}: {{ .nameVal }}
|
||||
{{ .address }}: {{ .addressVal }}
|
||||
{{ .time }}: {{ .timeVal }}
|
||||
|
||||
Note: Notification emails can be toggled on the admin dashboard.
|
||||
{{ .notificationNotice }}
|
||||
|
@ -20,8 +20,8 @@
|
||||
<mj-section mj-class="bg">
|
||||
<mj-column>
|
||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||
<h3>Your account was deleted.</h3>
|
||||
<p>Reason: <i>{{ .reason }}</i></p>
|
||||
<h3>{{ .yourAccountWasDeleted }}</h3>
|
||||
<p>{{ .reason }}: <i>{{ .reasonVal }}</i></p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
@ -1,4 +1,4 @@
|
||||
Your Jellyfin account was deleted.
|
||||
Reason: {{ .reason }}
|
||||
{{ .yourAccountWasDeleted }}
|
||||
{{ .reason }}: {{ .reasonVal }}
|
||||
|
||||
{{ .message }}
|
||||
|
@ -20,13 +20,13 @@
|
||||
<mj-section mj-class="bg">
|
||||
<mj-column>
|
||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||
<p>Hi {{ .username }},</p>
|
||||
<p> Someone has recently requested a password reset on Jellyfin.</p>
|
||||
<p>If this was you, enter the below pin into the prompt.</p>
|
||||
<p>The code will expire on {{ .expiry_date }}, at {{ .expiry_time }} UTC, which is in {{ .expires_in }}.</p>
|
||||
<p>If this wasn't you, please ignore this email.</p>
|
||||
<p>{{ .helloUser }}</p>
|
||||
<p>{{ .someoneHasRequestedReset }}</p>
|
||||
<p>{{ .ifItWasYou }}</p>
|
||||
<p>{{ .codeExpiry }}</p>
|
||||
<p>{{ .ifItWasNotYou }}</p>
|
||||
</mj-text>
|
||||
<mj-button mj-class="blue bold">{{ .pin }}</mj-button>
|
||||
<mj-button mj-class="blue bold">{{ .pinVal }}</mj-button>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section mj-class="bg2">
|
||||
|
@ -1,10 +1,10 @@
|
||||
Hi {{ .username }},
|
||||
{{ .helloUser }}
|
||||
|
||||
Someone has recently requests a password reset on Jellyfin.
|
||||
If this was you, enter the below pin into the prompt.
|
||||
This code will expire on {{ .expiry_date }}, at {{ .expiry_time }} UTC, which is in {{ .expires_in }}.
|
||||
If this wasn't you, please ignore this email.
|
||||
{{ .someoneHasRequestedReset }}
|
||||
{{ .ifItWasYou }}
|
||||
{{ .codeExpiry }}
|
||||
{{ .ifItWasNotYou }}
|
||||
|
||||
PIN: {{ .pin }}
|
||||
{{ .pin }}: {{ .pinVal }}
|
||||
|
||||
{{ .message }}
|
||||
|
@ -20,15 +20,15 @@
|
||||
<mj-section mj-class="bg">
|
||||
<mj-column>
|
||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||
<h3>Invite Expired.</h3>
|
||||
<p>Code {{ .code }} expired at {{ .expiry }}.</p>
|
||||
<h3>{{ .inviteExpired }}</h3>
|
||||
<p>{{ .expiredAt }}</p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section mj-class="bg2">
|
||||
<mj-column>
|
||||
<mj-text mj-class="secondary" font-style="italic" font-size="14px">
|
||||
Notification emails can be toggled on the admin dashboard.
|
||||
{{ .notificationNotice }}
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
@ -1,5 +1,5 @@
|
||||
Invite expired.
|
||||
{{ .inviteExpired }}
|
||||
|
||||
Code {{ .code }} expired at {{ .expiry }}.
|
||||
{{ .expiredAt }}
|
||||
|
||||
Note: Notification emails can be toggled on the admin dashboard.
|
||||
{{ .notificationNotice }}
|
||||
|
@ -20,12 +20,12 @@
|
||||
<mj-section mj-class="bg">
|
||||
<mj-column>
|
||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||
<p>Hi,</p>
|
||||
<h3>You've been invited to Jellyfin.</h3>
|
||||
<p>To join, click the button below.</p>
|
||||
<p>This invite will expire on {{ .expiry_date }}, at {{ .expiry_time }}, which is in {{ .expires_in }}, so act quick.</p>
|
||||
<p>{{ .hello }},</p>
|
||||
<h3>{{ .youHaveBeenInvited }}</h3>
|
||||
<p>{{ .toJoin }}</p>
|
||||
<p>{{ .inviteExpiry }}</p>
|
||||
</mj-text>
|
||||
<mj-button mj-class="blue bold" href="{{ .invite_link }}">Setup your account</mj-button>
|
||||
<mj-button mj-class="blue bold" href="{{ .invite_link }}">{{ .linkButton }}</mj-button>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section mj-class="bg2">
|
||||
|
@ -1,7 +1,7 @@
|
||||
Hi,
|
||||
You've been invited to Jellyfin.
|
||||
To join, follow the below link.
|
||||
This invite will expire on {{ .expiry_date }}, at {{ .expiry_time }}, which is in {{ .expires_in }}, so act quick.
|
||||
{{ .hello }},
|
||||
{{ .youHaveBeenInvited }}
|
||||
{{ .toJoin }}
|
||||
{{ .inviteExpiry }}
|
||||
|
||||
{{ .invite_link }}
|
||||
|
||||
|
2
main.go
2
main.go
@ -370,7 +370,6 @@ func start(asDaemon, firstCall bool) {
|
||||
|
||||
app.storage.profiles_path = app.config.Section("files").Key("user_profiles").String()
|
||||
app.storage.loadProfiles()
|
||||
|
||||
if !(len(app.storage.policy) == 0 && len(app.storage.configuration) == 0 && len(app.storage.displayprefs) == 0) {
|
||||
app.info.Println("Migrating user template files to new profile format")
|
||||
app.storage.migrateToProfile()
|
||||
@ -515,6 +514,7 @@ func start(asDaemon, firstCall bool) {
|
||||
}
|
||||
app.storage.lang.FormPath = filepath.Join(app.localPath, "lang", "form")
|
||||
app.storage.lang.AdminPath = filepath.Join(app.localPath, "lang", "admin")
|
||||
app.storage.lang.EmailPath = filepath.Join(app.localPath, "lang", "email")
|
||||
err = app.storage.loadLang()
|
||||
if err != nil {
|
||||
app.info.Fatalf("Failed to load language files: %+v\n", err)
|
||||
|
27
storage.go
27
storage.go
@ -20,14 +20,28 @@ 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)[lang][email][field].(string)
|
||||
for _, val := range vals {
|
||||
text = strings.Replace(text, "{n}", val, 1)
|
||||
}
|
||||
return text
|
||||
}
|
||||
func (el *EmailLang) get(lang, email, field string) string { return (*el)[lang][email][field].(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
|
||||
@ -80,7 +94,7 @@ func (st *Storage) loadLang() error {
|
||||
if err != nil {
|
||||
file = []byte("{}")
|
||||
}
|
||||
// Replace Jellyfin with emby on form
|
||||
// Replace Jellyfin with something if necessary
|
||||
if substituteStrings != "" {
|
||||
fileString := strings.ReplaceAll(string(file), "Jellyfin", substituteStrings)
|
||||
file = []byte(fileString)
|
||||
@ -121,6 +135,17 @@ func (st *Storage) loadLang() error {
|
||||
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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user