Make Emails translatable via transifex #6025
The transifex translation add in to the email templates. The optional environment variable NOTIFICATIONS_TRANSLATION_PATH added to config.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"strings"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed l10n/locale
|
||||
_translationFS embed.FS
|
||||
_domain = "notifications"
|
||||
)
|
||||
|
||||
// ComposeMessage renders the message based on template
|
||||
func ComposeMessage(template, locale string, path string) string {
|
||||
raw := loadTemplate(template, locale, path)
|
||||
return replacePlaceholders(raw)
|
||||
}
|
||||
|
||||
func loadTemplate(template, locale string, path string) string {
|
||||
// Create Locale with library path and language code and load default domain
|
||||
var l *gotext.Locale
|
||||
if path == "" {
|
||||
filesystem, _ := fs.Sub(_translationFS, "l10n/locale")
|
||||
l = gotext.NewLocaleFS(locale, filesystem)
|
||||
} else { // use custom path instead
|
||||
l = gotext.NewLocale(path, locale)
|
||||
}
|
||||
l.AddDomain(_domain) // make domain configurable only if needed
|
||||
return l.Get(template)
|
||||
}
|
||||
|
||||
func replacePlaceholders(raw string) string {
|
||||
for o, n := range _placeholders {
|
||||
raw = strings.ReplaceAll(raw, o, n)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
// Package email implements utility for rendering the Email.
|
||||
//
|
||||
// The email package supports transifex translation for email templates.
|
||||
package email
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"html"
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
)
|
||||
@@ -13,7 +17,33 @@ var (
|
||||
)
|
||||
|
||||
// RenderEmailTemplate renders the email template for a new share
|
||||
func RenderEmailTemplate(templateName string, templateVariables map[string]string, emailTemplatePath string) (string, error) {
|
||||
func RenderEmailTemplate(et MessageTemplate, locale string, emailTemplatePath string, translationPath string, vars map[string]interface{}) (string, string, error) {
|
||||
rawsub := ComposeMessage(et.Subject, locale, translationPath)
|
||||
// replace the body email placeholders with the values
|
||||
subject, err := executeRaw(rawsub, vars)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
bodyPlaceholders := map[string]interface{}{}
|
||||
bodyPlaceholders["Greeting"] = ComposeMessage(et.Greeting, locale, translationPath)
|
||||
bodyPlaceholders["MessageBody"] = ComposeMessage(et.MessageBody, locale, translationPath)
|
||||
bodyPlaceholders["CallToAction"] = ComposeMessage(et.CallToAction, locale, translationPath)
|
||||
|
||||
// replace the body email template placeholders with the translated template
|
||||
rawBody, err := executeEmailTemplate(emailTemplatePath, et.bodyTemplate, bodyPlaceholders)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// replace the body email placeholders with the values
|
||||
body, err := executeRaw(rawBody, vars)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return subject, body, nil
|
||||
}
|
||||
|
||||
func executeEmailTemplate(emailTemplatePath, templateName string, vars map[string]interface{}) (string, error) {
|
||||
var err error
|
||||
var tpl *template.Template
|
||||
// try to lookup the files in the filesystem
|
||||
@@ -25,10 +55,25 @@ func RenderEmailTemplate(templateName string, templateVariables map[string]strin
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
var writer bytes.Buffer
|
||||
err = tpl.Execute(&writer, templateVariables)
|
||||
str, err := executeTemplate(tpl, vars)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return html.UnescapeString(str), err
|
||||
}
|
||||
|
||||
func executeRaw(raw string, vars map[string]interface{}) (string, error) {
|
||||
tpl, err := template.New("").Parse(raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return executeTemplate(tpl, vars)
|
||||
}
|
||||
|
||||
func executeTemplate(tpl *template.Template, vars map[string]interface{}) (string, error) {
|
||||
var writer bytes.Buffer
|
||||
if err := tpl.Execute(&writer, vars); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return writer.String(), nil
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[o:owncloud-org:p:owncloud:r:ocis-notifications]
|
||||
file_filter = locale/<lang>/LC_MESSAGES/notifications.po
|
||||
minimum_perc = 75
|
||||
source_file = notifications.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
@@ -0,0 +1,102 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr "Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2023-04-14 16:58+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. UnsharedSpace email template, CallToAction field
|
||||
#: pkg/email/templates.go:57
|
||||
msgid "Click here to check it: {ShareLink}"
|
||||
msgstr ""
|
||||
|
||||
#. ShareCreated email template, CallToAction field
|
||||
#. SharedSpace email template, CallToAction field
|
||||
#: pkg/email/templates.go:18 pkg/email/templates.go:43
|
||||
msgid "Click here to view it: {ShareLink}"
|
||||
msgstr ""
|
||||
|
||||
#. ShareCreated email template, Greeting field
|
||||
#: pkg/email/templates.go:14
|
||||
msgid "Hello {ShareGrantee}"
|
||||
msgstr ""
|
||||
|
||||
#. ShareExpired email template, Greeting field
|
||||
#: pkg/email/templates.go:26
|
||||
msgid "Hello {ShareGrantee},"
|
||||
msgstr ""
|
||||
|
||||
#. SharedSpace email template, Greeting field
|
||||
#. UnsharedSpace email template, Greeting field
|
||||
#. MembershipExpired email template, Greeting field
|
||||
#: pkg/email/templates.go:39 pkg/email/templates.go:51 pkg/email/templates.go:65
|
||||
msgid "Hello {SpaceGrantee},"
|
||||
msgstr ""
|
||||
|
||||
#. MembershipExpired email template, Subject field
|
||||
#: pkg/email/templates.go:63
|
||||
msgid "Membership of '{SpaceName}' expired at {ExpiredAt}"
|
||||
msgstr ""
|
||||
|
||||
#. ShareExpired email template, Subject field
|
||||
#: pkg/email/templates.go:24
|
||||
msgid "Share to '{ShareFolder}' expired at {ExpiredAt}"
|
||||
msgstr ""
|
||||
|
||||
#. MembershipExpired email template, Message field
|
||||
#: pkg/email/templates.go:67
|
||||
msgid "Your membership of space {SpaceName} has expired at {ExpiredAt}\n"
|
||||
"\n"
|
||||
"Even though this membership has expired you still might have access through other shares and/or space memberships"
|
||||
msgstr ""
|
||||
|
||||
#. ShareExpired email template, Message field
|
||||
#: pkg/email/templates.go:28
|
||||
msgid "Your share to {ShareFolder} has expired at {ExpiredAt}\n"
|
||||
"\n"
|
||||
"Even though this share has been revoked you still might have access through other shares and/or space memberships."
|
||||
msgstr ""
|
||||
|
||||
#. ShareCreated email template, Message field
|
||||
#: pkg/email/templates.go:16
|
||||
msgid "{ShareSharer} has shared \"{ShareFolder}\" with you."
|
||||
msgstr ""
|
||||
|
||||
#. ShareCreated email template, Subject field
|
||||
#: pkg/email/templates.go:12
|
||||
msgid "{ShareSharer} shared '{ShareFolder}' with you"
|
||||
msgstr ""
|
||||
|
||||
#. SharedSpace email template, Message field
|
||||
#: pkg/email/templates.go:41
|
||||
msgid "{SpaceSharer} has invited you to join \"{SpaceName}\"."
|
||||
msgstr ""
|
||||
|
||||
#. UnsharedSpace email template, Message field
|
||||
#: pkg/email/templates.go:53
|
||||
msgid "{SpaceSharer} has removed you from \"{SpaceName}\".\n"
|
||||
"\n"
|
||||
"You might still have access through your other groups or direct membership."
|
||||
msgstr ""
|
||||
|
||||
#. SharedSpace email template, Subject field
|
||||
#: pkg/email/templates.go:37
|
||||
msgid "{SpaceSharer} invited you to join {SpaceName}"
|
||||
msgstr ""
|
||||
|
||||
#. UnsharedSpace email template, Subject field
|
||||
#: pkg/email/templates.go:49
|
||||
msgid "{SpaceSharer} removed you from {SpaceName}"
|
||||
msgstr ""
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package email
|
||||
|
||||
// Template marks the string as a translatable template
|
||||
func Template(s string) string { return s }
|
||||
|
||||
// the available templates
|
||||
var (
|
||||
// Shares
|
||||
ShareCreated = MessageTemplate{
|
||||
bodyTemplate: "shares/shareCreated.email.body.tmpl",
|
||||
// ShareCreated email template, Subject field
|
||||
Subject: Template(`{ShareSharer} shared '{ShareFolder}' with you`),
|
||||
// ShareCreated email template, Greeting field
|
||||
Greeting: Template(`Hello {ShareGrantee}`),
|
||||
// ShareCreated email template, Message field
|
||||
MessageBody: Template(`{ShareSharer} has shared "{ShareFolder}" with you.`),
|
||||
// ShareCreated email template, CallToAction field
|
||||
CallToAction: Template(`Click here to view it: {ShareLink}`),
|
||||
}
|
||||
|
||||
ShareExpired = MessageTemplate{
|
||||
bodyTemplate: "shares/shareExpired.email.body.tmpl",
|
||||
// ShareExpired email template, Subject field
|
||||
Subject: Template(`Share to '{ShareFolder}' expired at {ExpiredAt}`),
|
||||
// ShareExpired email template, Greeting field
|
||||
Greeting: Template(`Hello {ShareGrantee},`),
|
||||
// ShareExpired email template, Message field
|
||||
MessageBody: Template(`Your share to {ShareFolder} has expired at {ExpiredAt}
|
||||
|
||||
Even though this share has been revoked you still might have access through other shares and/or space memberships.`),
|
||||
}
|
||||
|
||||
// Spaces templates
|
||||
SharedSpace = MessageTemplate{
|
||||
bodyTemplate: "spaces/sharedSpace.email.body.tmpl",
|
||||
// SharedSpace email template, Subject field
|
||||
Subject: Template("{SpaceSharer} invited you to join {SpaceName}"),
|
||||
// SharedSpace email template, Greeting field
|
||||
Greeting: Template(`Hello {SpaceGrantee},`),
|
||||
// SharedSpace email template, Message field
|
||||
MessageBody: Template(`{SpaceSharer} has invited you to join "{SpaceName}".`),
|
||||
// SharedSpace email template, CallToAction field
|
||||
CallToAction: Template(`Click here to view it: {ShareLink}`),
|
||||
}
|
||||
|
||||
UnsharedSpace = MessageTemplate{
|
||||
bodyTemplate: "spaces/unsharedSpace.email.body.tmpl",
|
||||
// UnsharedSpace email template, Subject field
|
||||
Subject: Template(`{SpaceSharer} removed you from {SpaceName}`),
|
||||
// UnsharedSpace email template, Greeting field
|
||||
Greeting: Template(`Hello {SpaceGrantee},`),
|
||||
// UnsharedSpace email template, Message field
|
||||
MessageBody: Template(`{SpaceSharer} has removed you from "{SpaceName}".
|
||||
|
||||
You might still have access through your other groups or direct membership.`),
|
||||
// UnsharedSpace email template, CallToAction field
|
||||
CallToAction: Template(`Click here to check it: {ShareLink}`),
|
||||
}
|
||||
|
||||
MembershipExpired = MessageTemplate{
|
||||
bodyTemplate: "spaces/membershipExpired.email.body.tmpl",
|
||||
// MembershipExpired email template, Subject field
|
||||
Subject: Template(`Membership of '{SpaceName}' expired at {ExpiredAt}`),
|
||||
// MembershipExpired email template, Greeting field
|
||||
Greeting: Template(`Hello {SpaceGrantee},`),
|
||||
// MembershipExpired email template, Message field
|
||||
MessageBody: Template(`Your membership of space {SpaceName} has expired at {ExpiredAt}
|
||||
|
||||
Even though this membership has expired you still might have access through other shares and/or space memberships`),
|
||||
}
|
||||
)
|
||||
|
||||
// holds the information to turn the raw template into a parseable go template
|
||||
var _placeholders = map[string]string{
|
||||
"{ShareSharer}": "{{ .ShareSharer }}",
|
||||
"{ShareFolder}": "{{ .ShareFolder }}",
|
||||
"{ShareGrantee}": "{{ .ShareGrantee }}",
|
||||
"{ShareLink}": "{{ .ShareLink }}",
|
||||
"{SpaceName}": "{{ .SpaceName }}",
|
||||
"{SpaceGrantee}": "{{ .SpaceGrantee }}",
|
||||
"{SpaceSharer}": "{{ .SpaceSharer }}",
|
||||
"{ExpiredAt}": "{{ .ExpiredAt }}",
|
||||
}
|
||||
|
||||
// MessageTemplate is the data structure for the email
|
||||
type MessageTemplate struct {
|
||||
// bodyTemplate represent the path to .tmpl file
|
||||
bodyTemplate string
|
||||
Subject string
|
||||
Greeting string
|
||||
MessageBody string
|
||||
CallToAction string
|
||||
}
|
||||
@@ -1,16 +1,8 @@
|
||||
Hello {{ .ShareGrantee }},
|
||||
{{ .Greeting }}
|
||||
|
||||
{{ .ShareSharer }} has shared "{{ .ShareFolder }}" with you.
|
||||
{{ .MessageBody }}
|
||||
|
||||
Click here to view it: {{ .ShareLink }}
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
Hallo {{ .ShareGrantee }},
|
||||
|
||||
{{ .ShareSharer }} hat dich zu "{{ .ShareFolder }}" eingeladen.
|
||||
|
||||
Klicke hier zum Anzeigen: {{ .ShareLink }}
|
||||
{{ .CallToAction }}
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{ .ShareSharer }} shared '{{ .ShareFolder }}' with you
|
||||
@@ -1,16 +1,6 @@
|
||||
Hello {{ .ShareGrantee }},
|
||||
{{ .Greeting }}
|
||||
|
||||
Your share to {{ .ShareFolder }} has expired at {{ .ExpiredAt }}
|
||||
|
||||
Even though this share has been revoked you still might have access through other shares and/or space memberships
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
Hallo {{ .ShareGrantee }},
|
||||
|
||||
Deine Freigabe zu {{ .ShareFolder }} ist am {{ .ExpiredAt }} abgelaufen.
|
||||
|
||||
Obwohl diese Freigabe nicht mehr zur Verfügung steht, könntest du immer noch Zugriff über andere Freigaben und/oder Space Mitgliedschaften haben.
|
||||
{{ .MessageBody }}
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Share to '{{ .ShareFolder }}' expired at {{ .ExpiredAt }}
|
||||
+2
-12
@@ -1,16 +1,6 @@
|
||||
Hello {{ .SpaceGrantee }},
|
||||
{{ .Greeting }}
|
||||
|
||||
Your membership of space {{ .SpaceName }} has expired at {{ .ExpiredAt }}
|
||||
|
||||
Even though this membership has expired you still might have access through other shares and/or space memberships
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
Hallo {{ .SpaceGrantee }},
|
||||
|
||||
Deine Mitgliedschaft zu dem Space {{ .SpaceName }} ist am {{ .ExpiredAt }} abgelaufen.
|
||||
|
||||
Obwohl diese Mitgliedschaft nicht mehr zur Verfügung steht, könntest du immer noch Zugriff über andere Freigaben und/oder Space Mitgliedschaften haben.
|
||||
{{ .MessageBody }}
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Membership of '{{ .SpaceName }}' expired at {{ .ExpiredAt }}
|
||||
@@ -1,16 +1,8 @@
|
||||
Hello {{ .SpaceGrantee }},
|
||||
{{ .Greeting }}
|
||||
|
||||
{{ .SpaceSharer }} has invited you to join "{{ .SpaceName }}".
|
||||
{{ .MessageBody }}
|
||||
|
||||
Click here to view it: {{ .ShareLink }}
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
Hallo {{ .SpaceGrantee }},
|
||||
|
||||
{{ .SpaceSharer }} hat dich in den Space "{{ .SpaceName }}" eingeladen.
|
||||
|
||||
Klicke hier zum Anzeigen: {{ .ShareLink }}
|
||||
{{ .CallToAction }}
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{ .SpaceSharer }} invited you to join {{ .SpaceName }}
|
||||
@@ -1,16 +1,8 @@
|
||||
Hello {{ .SpaceGrantee }},
|
||||
{{ .Greeting }}
|
||||
|
||||
{{ .SpaceSharer }} has removed you from "{{ .SpaceName }}".
|
||||
{{ .MessageBody }}
|
||||
|
||||
You might still have access through your other groups or direct membership. Click here to check it: {{ .ShareLink }}
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
Hallo {{ .SpaceGrantee }},
|
||||
|
||||
{{ .SpaceSharer }} hat dich aus dem Space "{{ .SpaceName }}" entfernt.
|
||||
|
||||
Du könntest über deine anderen Gruppen oder deiner direkten Mitgliedschaft noch Zugriff haben. Klicke hier zum Überprüfen: {{ .ShareLink }}
|
||||
{{ .CallToAction }}
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{ .SpaceSharer }} removed you from {{ .SpaceName }}
|
||||
Reference in New Issue
Block a user