Refactor i18n name and fix the L() function in public HTML templates
This commit is contained in:
parent
4cd5e6ebeb
commit
ee4fb7182f
15
cmd/init.go
15
cmd/init.go
|
@ -258,13 +258,13 @@ func initConstants() *constants {
|
||||||
|
|
||||||
// initI18n initializes a new i18n instance with the selected language map
|
// initI18n initializes a new i18n instance with the selected language map
|
||||||
// loaded from the filesystem.
|
// loaded from the filesystem.
|
||||||
func initI18n(lang string, fs stuffbin.FileSystem) *i18n.I18nLang {
|
func initI18n(lang string, fs stuffbin.FileSystem) *i18n.I18n {
|
||||||
b, err := fs.Read(fmt.Sprintf("/i18n/%s.json", lang))
|
b, err := fs.Read(fmt.Sprintf("/i18n/%s.json", lang))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lo.Fatalf("error loading i18n language file: %v", err)
|
lo.Fatalf("error loading i18n language file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
i, err := i18n.New(lang, b)
|
i, err := i18n.New(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lo.Fatalf("error unmarshalling i18n language: %v", err)
|
lo.Fatalf("error unmarshalling i18n language: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,7 @@ func initCampaignManager(q *Queries, cs *constants, app *App) *manager.Manager {
|
||||||
ViewTrackURL: cs.ViewTrackURL,
|
ViewTrackURL: cs.ViewTrackURL,
|
||||||
MessageURL: cs.MessageURL,
|
MessageURL: cs.MessageURL,
|
||||||
UnsubHeader: ko.Bool("privacy.unsubscribe_header"),
|
UnsubHeader: ko.Bool("privacy.unsubscribe_header"),
|
||||||
}, newManagerDB(q), campNotifCB, lo)
|
}, newManagerDB(q), campNotifCB, app.i18n, lo)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +428,7 @@ func initMediaStore() media.Store {
|
||||||
|
|
||||||
// initNotifTemplates compiles and returns e-mail notification templates that are
|
// initNotifTemplates compiles and returns e-mail notification templates that are
|
||||||
// used for sending ad-hoc notifications to admins and subscribers.
|
// used for sending ad-hoc notifications to admins and subscribers.
|
||||||
func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18nLang, cs *constants) *template.Template {
|
func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18n, cs *constants) *template.Template {
|
||||||
// Register utility functions that the e-mail templates can use.
|
// Register utility functions that the e-mail templates can use.
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"RootURL": func() string {
|
"RootURL": func() string {
|
||||||
|
@ -437,7 +437,7 @@ func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18nLang, c
|
||||||
"LogoURL": func() string {
|
"LogoURL": func() string {
|
||||||
return cs.LogoURL
|
return cs.LogoURL
|
||||||
},
|
},
|
||||||
"L": func() *i18n.I18nLang {
|
"L": func() *i18n.I18n {
|
||||||
return i
|
return i
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -464,7 +464,10 @@ func initHTTPServer(app *App) *echo.Echo {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Parse and load user facing templates.
|
// Parse and load user facing templates.
|
||||||
tpl, err := stuffbin.ParseTemplatesGlob(nil, app.fs, "/public/templates/*.html")
|
tpl, err := stuffbin.ParseTemplatesGlob(template.FuncMap{
|
||||||
|
"L": func() *i18n.I18n {
|
||||||
|
return app.i18n
|
||||||
|
}}, app.fs, "/public/templates/*.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lo.Fatalf("error parsing public templates: %v", err)
|
lo.Fatalf("error parsing public templates: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ type App struct {
|
||||||
importer *subimporter.Importer
|
importer *subimporter.Importer
|
||||||
messengers map[string]messenger.Messenger
|
messengers map[string]messenger.Messenger
|
||||||
media media.Store
|
media media.Store
|
||||||
i18n *i18n.I18nLang
|
i18n *i18n.I18n
|
||||||
notifTpls *template.Template
|
notifTpls *template.Template
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
bufLog *buflog.BufLog
|
bufLog *buflog.BufLog
|
||||||
|
|
|
@ -39,7 +39,7 @@ type tplData struct {
|
||||||
LogoURL string
|
LogoURL string
|
||||||
FaviconURL string
|
FaviconURL string
|
||||||
Data interface{}
|
Data interface{}
|
||||||
L *i18n.I18nLang
|
L *i18n.I18n
|
||||||
}
|
}
|
||||||
|
|
||||||
type publicTpl struct {
|
type publicTpl struct {
|
||||||
|
|
14
i18n/en.json
14
i18n/en.json
|
@ -83,6 +83,7 @@
|
||||||
"globals.buttons.cancel": "Cancel",
|
"globals.buttons.cancel": "Cancel",
|
||||||
"globals.buttons.clone": "Clone",
|
"globals.buttons.clone": "Clone",
|
||||||
"globals.buttons.close": "Close",
|
"globals.buttons.close": "Close",
|
||||||
|
"globals.buttons.continue": "Continue",
|
||||||
"globals.buttons.delete": "Delete",
|
"globals.buttons.delete": "Delete",
|
||||||
"globals.buttons.edit": "Edit",
|
"globals.buttons.edit": "Edit",
|
||||||
"globals.buttons.enabled": "Enabled",
|
"globals.buttons.enabled": "Enabled",
|
||||||
|
@ -188,14 +189,25 @@
|
||||||
"menu.media": "Media",
|
"menu.media": "Media",
|
||||||
"menu.newCampaign": "Create new",
|
"menu.newCampaign": "Create new",
|
||||||
"menu.settings": "Settings",
|
"menu.settings": "Settings",
|
||||||
|
"public.subNotFound": "Subscription not found.",
|
||||||
"public.campaignNotFound": "The e-mail message was not found.",
|
"public.campaignNotFound": "The e-mail message was not found.",
|
||||||
|
"public.unsubTitle": "Unsubscribe",
|
||||||
|
"public.unsubHelp": "Do you want to unsubscribe from this mailing list?",
|
||||||
|
"public.unsubFull": "Also unsubscribe from all future e-mails.",
|
||||||
|
"public.unsub": "Unsubscribe",
|
||||||
|
"public.privacyTitle": "Privacy and data",
|
||||||
|
"public.privacyExport": "Export your data",
|
||||||
|
"public.privacyExportHelp": "A copy of your data will be e-mailed to you.",
|
||||||
|
"public.privacyWipe": "Wipe your data",
|
||||||
|
"public.privacyWipeHelp": "Delete all your subscriptions and related data from the database permanently.",
|
||||||
|
"public.privacyConfirmWipe": "Are you sure you want to delete all your subscription data permanently?",
|
||||||
"public.confirmOptinSubTitle": "Confirm subscription",
|
"public.confirmOptinSubTitle": "Confirm subscription",
|
||||||
"public.confirmSub": "Confirm subscription",
|
"public.confirmSub": "Confirm subscription",
|
||||||
"public.confirmSubInfo": "You have been added to the following lists:",
|
"public.confirmSubInfo": "You have been added to the following lists:",
|
||||||
"public.confirmSubTitle": "Confirm",
|
"public.confirmSubTitle": "Confirm",
|
||||||
"public.dataRemoved": "Your subscriptions and all associated data has been removed.",
|
"public.dataRemoved": "Your subscriptions and all associated data has been removed.",
|
||||||
"public.dataRemovedTitle": "Data removed",
|
"public.dataRemovedTitle": "Data removed",
|
||||||
"public.dataSent": "Your data has been e-mailed to you as an attachment",
|
"public.dataSent": "Your data has been e-mailed to you as an attachment.",
|
||||||
"public.dataSentTitle": "Data e-mailed",
|
"public.dataSentTitle": "Data e-mailed",
|
||||||
"public.errorFetchingCampaign": "Error fetching e-mail message",
|
"public.errorFetchingCampaign": "Error fetching e-mail message",
|
||||||
"public.errorFetchingEmail": "E-mail message not found",
|
"public.errorFetchingEmail": "E-mail message not found",
|
||||||
|
|
|
@ -1,48 +1,66 @@
|
||||||
|
// i18n is a simple package that translates strings using a language map.
|
||||||
|
// It mimicks some functionality of the vue-i18n library so that the same JSON
|
||||||
|
// language map may be used in the JS frontent and the Go backend.
|
||||||
package i18n
|
package i18n
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lang represents a loaded language.
|
// I18n offers translation functions over a language map.
|
||||||
type Lang struct {
|
type I18n struct {
|
||||||
Code string `json:"code"`
|
code string `json:"code"`
|
||||||
Name string `json:"name"`
|
name string `json:"name"`
|
||||||
langMap map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// I18nLang is a simple i18n library that translates strings using a language map.
|
|
||||||
// It mimicks some functionality of the vue-i18n library so that the same JSON
|
|
||||||
// language map may be used in the JS frontent and the Go backend.
|
|
||||||
type I18nLang struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
langMap map[string]string
|
langMap map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
var reParam = regexp.MustCompile(`(?i)\{([a-z0-9-.]+)\}`)
|
var reParam = regexp.MustCompile(`(?i)\{([a-z0-9-.]+)\}`)
|
||||||
|
|
||||||
// New returns an I18n instance.
|
// New returns an I18n instance.
|
||||||
func New(code string, b []byte) (*I18nLang, error) {
|
func New(b []byte) (*I18n, error) {
|
||||||
var l map[string]string
|
var l map[string]string
|
||||||
if err := json.Unmarshal(b, &l); err != nil {
|
if err := json.Unmarshal(b, &l); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &I18nLang{
|
|
||||||
|
code, ok := l["_.code"]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("missing _.code field in language file")
|
||||||
|
}
|
||||||
|
|
||||||
|
name, ok := l["_.name"]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("missing _.name field in language file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &I18n{
|
||||||
langMap: l,
|
langMap: l,
|
||||||
|
code: code,
|
||||||
|
name: name,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the canonical name of the language.
|
||||||
|
func (i *I18n) Name() string {
|
||||||
|
return i.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code returns the ISO code of the language.
|
||||||
|
func (i *I18n) Code() string {
|
||||||
|
return i.code
|
||||||
|
}
|
||||||
|
|
||||||
// JSON returns the languagemap as raw JSON.
|
// JSON returns the languagemap as raw JSON.
|
||||||
func (i *I18nLang) JSON() []byte {
|
func (i *I18n) JSON() []byte {
|
||||||
b, _ := json.Marshal(i.langMap)
|
b, _ := json.Marshal(i.langMap)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// T returns the translation for the given key similar to vue i18n's t().
|
// T returns the translation for the given key similar to vue i18n's t().
|
||||||
func (i *I18nLang) T(key string) string {
|
func (i *I18n) T(key string) string {
|
||||||
s, ok := i.langMap[key]
|
s, ok := i.langMap[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return key
|
return key
|
||||||
|
@ -59,7 +77,7 @@ func (i *I18nLang) T(key string) string {
|
||||||
// eg: Ts("globals.message.notFound",
|
// eg: Ts("globals.message.notFound",
|
||||||
// "name", "campaigns",
|
// "name", "campaigns",
|
||||||
// "error", err)
|
// "error", err)
|
||||||
func (i *I18nLang) Ts(key string, params ...string) string {
|
func (i *I18n) Ts(key string, params ...string) string {
|
||||||
if len(params)%2 != 0 {
|
if len(params)%2 != 0 {
|
||||||
return key + `: Invalid arguments`
|
return key + `: Invalid arguments`
|
||||||
}
|
}
|
||||||
|
@ -82,7 +100,7 @@ func (i *I18nLang) Ts(key string, params ...string) string {
|
||||||
// Tc returns the translation for the given key similar to vue i18n's tc().
|
// Tc returns the translation for the given key similar to vue i18n's tc().
|
||||||
// It expects the language string in the map to be of the form `Singular | Plural` and
|
// It expects the language string in the map to be of the form `Singular | Plural` and
|
||||||
// returns `Plural` if n > 1, or `Singular` otherwise.
|
// returns `Plural` if n > 1, or `Singular` otherwise.
|
||||||
func (i *I18nLang) Tc(key string, n int) string {
|
func (i *I18n) Tc(key string, n int) string {
|
||||||
s, ok := i.langMap[key]
|
s, ok := i.langMap[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return key
|
return key
|
||||||
|
@ -98,7 +116,7 @@ func (i *I18nLang) Tc(key string, n int) string {
|
||||||
|
|
||||||
// getSingular returns the singular term from the vuei18n pipe separated value.
|
// getSingular returns the singular term from the vuei18n pipe separated value.
|
||||||
// singular term | plural term
|
// singular term | plural term
|
||||||
func (i *I18nLang) getSingular(s string) string {
|
func (i *I18n) getSingular(s string) string {
|
||||||
if !strings.Contains(s, "|") {
|
if !strings.Contains(s, "|") {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -108,7 +126,7 @@ func (i *I18nLang) getSingular(s string) string {
|
||||||
|
|
||||||
// getSingular returns the plural term from the vuei18n pipe separated value.
|
// getSingular returns the plural term from the vuei18n pipe separated value.
|
||||||
// singular term | plural term
|
// singular term | plural term
|
||||||
func (i *I18nLang) getPlural(s string) string {
|
func (i *I18n) getPlural(s string) string {
|
||||||
if !strings.Contains(s, "|") {
|
if !strings.Contains(s, "|") {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -122,7 +140,7 @@ func (i *I18nLang) getPlural(s string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// subAllParams recursively resolves and replaces all {params} in a string.
|
// subAllParams recursively resolves and replaces all {params} in a string.
|
||||||
func (i *I18nLang) subAllParams(s string) string {
|
func (i *I18n) subAllParams(s string) string {
|
||||||
if !strings.Contains(s, `{`) {
|
if !strings.Contains(s, `{`) {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/knadh/listmonk/internal/i18n"
|
||||||
"github.com/knadh/listmonk/internal/messenger"
|
"github.com/knadh/listmonk/internal/messenger"
|
||||||
"github.com/knadh/listmonk/models"
|
"github.com/knadh/listmonk/models"
|
||||||
)
|
)
|
||||||
|
@ -40,6 +41,7 @@ type DataSource interface {
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
src DataSource
|
src DataSource
|
||||||
|
i18n *i18n.I18n
|
||||||
messengers map[string]messenger.Messenger
|
messengers map[string]messenger.Messenger
|
||||||
notifCB models.AdminNotifCallback
|
notifCB models.AdminNotifCallback
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
|
@ -108,7 +110,7 @@ type msgError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new instance of Mailer.
|
// New returns a new instance of Mailer.
|
||||||
func New(cfg Config, src DataSource, notifCB models.AdminNotifCallback, l *log.Logger) *Manager {
|
func New(cfg Config, src DataSource, notifCB models.AdminNotifCallback, i *i18n.I18n, l *log.Logger) *Manager {
|
||||||
if cfg.BatchSize < 1 {
|
if cfg.BatchSize < 1 {
|
||||||
cfg.BatchSize = 1000
|
cfg.BatchSize = 1000
|
||||||
}
|
}
|
||||||
|
@ -122,6 +124,7 @@ func New(cfg Config, src DataSource, notifCB models.AdminNotifCallback, l *log.L
|
||||||
return &Manager{
|
return &Manager{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
src: src,
|
src: src,
|
||||||
|
i18n: i,
|
||||||
notifCB: notifCB,
|
notifCB: notifCB,
|
||||||
logger: l,
|
logger: l,
|
||||||
messengers: make(map[string]messenger.Messenger),
|
messengers: make(map[string]messenger.Messenger),
|
||||||
|
@ -334,6 +337,9 @@ func (m *Manager) TemplateFuncs(c *models.Campaign) template.FuncMap {
|
||||||
}
|
}
|
||||||
return time.Now().Format(layout)
|
return time.Now().Format(layout)
|
||||||
},
|
},
|
||||||
|
"L": func() *i18n.I18n {
|
||||||
|
return m.i18n
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
{{ define "campaign-status" }}
|
{{ define "campaign-status" }}
|
||||||
{{ template "header" . }}
|
{{ template "header" . }}
|
||||||
<h2>{{ .L.T "email.status.campaignUpdate" }}</h2>
|
<h2>{{ L.Ts "email.status.campaignUpdate" }}</h2>
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="30%"><strong>{{ .L.T "globa.L.Terms.campaign" }}</strong></td>
|
<td width="30%"><strong>{{ L.Ts "globa.L.Terms.campaign" }}</strong></td>
|
||||||
<td><a href="{{ index . "RootURL" }}/campaigns/{{ index . "ID" }}">{{ index . "Name" }}</a></td>
|
<td><a href="{{ index . "RootURL" }}/campaigns/{{ index . "ID" }}">{{ index . "Name" }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="30%"><strong>{{ .L.T "email.status.status" }}</strong></td>
|
<td width="30%"><strong>{{ L.Ts "email.status.status" }}</strong></td>
|
||||||
<td>{{ index . "Status" }}</td>
|
<td>{{ index . "Status" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="30%"><strong>{{ .L.T "email.status.campaignSent" }}</strong></td>
|
<td width="30%"><strong>{{ L.Ts "email.status.campaignSent" }}</strong></td>
|
||||||
<td>{{ index . "Sent" }} / {{ index . "ToSend" }}</td>
|
<td>{{ index . "Sent" }} / {{ index . "ToSend" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{ if ne (index . "Reason") "" }}
|
{{ if ne (index . "Reason") "" }}
|
||||||
<tr>
|
<tr>
|
||||||
<td width="30%"><strong>{{ .L.T "email.status.campaignReason" }}</strong></td>
|
<td width="30%"><strong>{{ L.Ts "email.status.campaignReason" }}</strong></td>
|
||||||
<td>{{ index . "Reason" }}</td>
|
<td>{{ index . "Reason" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -77,8 +77,8 @@
|
||||||
|
|
||||||
<div class="footer" style="text-align: center;font-size: 12px;color: #888;">
|
<div class="footer" style="text-align: center;font-size: 12px;color: #888;">
|
||||||
<p>
|
<p>
|
||||||
{{ I18n.T "email.unsubHelp" }}
|
{{ L.T "email.unsubHelp" }}
|
||||||
<a href="{{ UnsubscribeURL }}" style="color: #888;">{{ I18n.T "email.unsub" }}</a>
|
<a href="{{ UnsubscribeURL }}" style="color: #888;">{{ L.T "email.unsub" }}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>Powered by <a href="https://listmonk.app" target="_blank" style="color: #888;">listmonk</a></p>
|
<p>Powered by <a href="https://listmonk.app" target="_blank" style="color: #888;">listmonk</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
{{ define "import-status" }}
|
{{ define "import-status" }}
|
||||||
{{ template "header" . }}
|
{{ template "header" . }}
|
||||||
<h2>{{ .L.T "email.status.importTitle" }}</h2>
|
<h2>{{ L.Ts "email.status.importTitle" }}</h2>
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="30%"><strong>{{ .L.T "email.status.importFile" }}</strong></td>
|
<td width="30%"><strong>{{ L.Ts "email.status.importFile" }}</strong></td>
|
||||||
<td><a href="{{ RootURL }}/subscribers/import">{{ .Name }}</a></td>
|
<td><a href="{{ RootURL }}/subscribers/import">{{ .Name }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="30%"><strong>{{ .L.T "email.status.status" }}</strong></td>
|
<td width="30%"><strong>{{ L.Ts "email.status.status" }}</strong></td>
|
||||||
<td>{{ .Status }}</td>
|
<td>{{ .Status }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="30%"><strong>{{ .L.T "email.status.importRecords" }}</strong></td>
|
<td width="30%"><strong>{{ L.Ts "email.status.importRecords" }}</strong></td>
|
||||||
<td>{{ .Imported }} / {{ .Total }}</td>
|
<td>{{ .Imported }} / {{ .Total }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{{ define "subscriber-data" }}
|
{{ define "subscriber-data" }}
|
||||||
{{ template "header" . }}
|
{{ template "header" . }}
|
||||||
<h2>{{ .L.T "email.data.title" }}</h2>
|
<h2>{{ L.Ts "email.data.title" }}</h2>
|
||||||
<p>
|
<p>
|
||||||
{{ .L.T "email.data.info" }}
|
{{ L.Ts "email.data.info" }}
|
||||||
</p>
|
</p>
|
||||||
{{ template "footer" }}
|
{{ template "footer" }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
{{ define "optin-campaign" }}
|
{{ define "optin-campaign" }}
|
||||||
|
|
||||||
<p>{{ .L.Ts "email.optin.confirmSubWelcome" "name" .Subscriber.FirstName }}</p>
|
<p>{{ L.Ts "email.optin.confirmSubWelcome" "name" .Subscriber.FirstName }}</p>
|
||||||
<p>{{ .L.T "email.optin.confirmSubInfo" }}</p>
|
<p>{{ L.Ts "email.optin.confirmSubInfo" }}</p>
|
||||||
<ul>
|
<ul>
|
||||||
{{ range $i, $l := .Lists }}
|
{{ range $i, $l := .Lists }}
|
||||||
{{ if eq .Type "public" }}
|
{{ if eq .Type "public" }}
|
||||||
<li>{{ .Name }}</li>
|
<li>{{ .Name }}</li>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<li>{{ .L.T "email.optin.privateList" }}</li>
|
<li>{{ L.Ts "email.optin.privateList" }}</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
<a class="button" {{ .OptinURLAttr }} class="button">{{ .L.T "email.optin.confirmSub" }}</a>
|
<a class="button" {{ .OptinURLAttr }} class="button">{{ L.Ts "email.optin.confirmSub" }}</a>
|
||||||
</p>
|
</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
{{ define "subscriber-optin" }}
|
{{ define "subscriber-optin" }}
|
||||||
{{ template "header" . }}
|
{{ template "header" . }}
|
||||||
<h2>{{ .L.T "email.optin.confirmSubTitle" }}</h2>
|
<h2>{{ L.Ts "email.optin.confirmSubTitle" }}</h2>
|
||||||
<p>{{ .L.Ts "email.optin.confirmSubWelcome" "name" .Subscriber.FirstName }}</p>
|
<p>{{ L.Ts "email.optin.confirmSubWelcome" "name" .Subscriber.FirstName }}</p>
|
||||||
<p>{{ .L.T "email.optin.confirmSubInfo" }}</p>
|
<p>{{ L.Ts "email.optin.confirmSubInfo" }}</p>
|
||||||
<ul>
|
<ul>
|
||||||
{{ range $i, $l := .Lists }}
|
{{ range $i, $l := .Lists }}
|
||||||
{{ if eq .Type "public" }}
|
{{ if eq .Type "public" }}
|
||||||
<li>{{ .Name }}</li>
|
<li>{{ .Name }}</li>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<li>{{ .L.T "email.optin.privateList" }}</li>
|
<li>{{ L.Ts "email.optin.privateList" }}</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
<p>{{ .L.T "email.optin.confirmSubHelp" }}</p>
|
<p>{{ L.Ts "email.optin.confirmSubHelp" }}</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ .OptinURL }}" class="button">{{ .L.T "email.optin.confirmSub" }}</a>
|
<a href="{{ .OptinURL }}" class="button">{{ L.Ts "email.optin.confirmSub" }}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{{ template "footer" }}
|
{{ template "footer" }}
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
{{ define "optin" }}
|
{{ define "optin" }}
|
||||||
{{ template "header" .}}
|
{{ template "header" .}}
|
||||||
<section>
|
<section>
|
||||||
<h2>{{ .L.T "public.confirmSubTitle" }}</h2>
|
<h2>{{ L.T "public.confirmSubTitle" }}</h2>
|
||||||
<p>
|
<p>
|
||||||
{{ .L.T "public.confirmSubInfo" }}
|
{{ L.T "public.confirmSubInfo" }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<ul>
|
<ul>
|
||||||
{{ range $i, $l := .Data.Lists }}
|
{{ range $i, $l := .Data.Lists }}
|
||||||
<input type="hidden" name="l" value="{{ $l.UUID }}" />
|
<input type="hidden" name="l" value="{{ $l.UUID }}" />
|
||||||
{{ if eq $.L.Type "public" }}
|
{{ if eq $l.Type "public" }}
|
||||||
<li>{{ $l.Name }}</li>
|
<li>{{ $l.Name }}</li>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<li>{{ .L.T "public.subPrivateList" }}</li>
|
<li>{{ L.Ts "public.subPrivateList" }}</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
<input type="hidden" name="confirm" value="true" />
|
<input type="hidden" name="confirm" value="true" />
|
||||||
<button type="submit" class="button" id="btn-unsub">
|
<button type="submit" class="button" id="btn-unsub">
|
||||||
{{ .L.T "public.confirmSub" }}
|
{{ L.Ts "public.confirmSub" }}
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
{{ define "subscription" }}
|
{{ define "subscription" }}
|
||||||
{{ template "header" .}}
|
{{ template "header" .}}
|
||||||
<section>
|
<section>
|
||||||
<h2>Unsubscribe</h2>
|
<h2>{{ L.T "public.unsubTitle" }}</h2>
|
||||||
<p>Do you wish to unsubscribe from this mailing list?</p>
|
<p>{{ L.T "public.unsubHelp" }}</p>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<div>
|
<div>
|
||||||
{{ if .Data.AllowBlocklist }}
|
{{ if .Data.AllowBlocklist }}
|
||||||
<p>
|
<p>
|
||||||
<input id="privacy-blocklist" type="checkbox" name="blocklist" value="true" /> <label for="privacy-blocklist">Also unsubscribe from all future e-mails.</label>
|
<input id="privacy-blocklist" type="checkbox" name="blocklist" value="true" />
|
||||||
|
<label for="privacy-blocklist">{{ L.T "public.unsubFull" }}</label>
|
||||||
</p>
|
</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button type="submit" class="button" id="btn-unsub">Unsubscribe</button>
|
<button type="submit" class="button" id="btn-unsub">{{ L.T "public.unsub" }}</button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -21,16 +22,16 @@
|
||||||
{{ if or .Data.AllowExport .Data.AllowWipe }}
|
{{ if or .Data.AllowExport .Data.AllowWipe }}
|
||||||
<form id="data-form" method="post" action="" onsubmit="return handleData()">
|
<form id="data-form" method="post" action="" onsubmit="return handleData()">
|
||||||
<section>
|
<section>
|
||||||
<h2>Privacy and data</h2>
|
<h2>{{ L.T "public.privacyTitle" }}</h2>
|
||||||
{{ if .Data.AllowExport }}
|
{{ if .Data.AllowExport }}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="one columns">
|
<div class="one columns">
|
||||||
<input id="privacy-export" type="radio" name="data-action" value="export" required />
|
<input id="privacy-export" type="radio" name="data-action" value="export" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="ten columns">
|
<div class="ten columns">
|
||||||
<label for="privacy-export"><strong>Export your data</strong></label>
|
<label for="privacy-export"><strong>{{ L.T "public.privacyExport" }}</strong></label>
|
||||||
<br />
|
<br />
|
||||||
A copy of your data will be e-mailed to you.
|
{{ L.T "public.privacyExportHelp" }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -41,14 +42,14 @@
|
||||||
<input id="privacy-wipe" type="radio" name="data-action" value="wipe" required />
|
<input id="privacy-wipe" type="radio" name="data-action" value="wipe" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="ten columns">
|
<div class="ten columns">
|
||||||
<label for="privacy-wipe"><strong>Wipe your data</strong></label>
|
<label for="privacy-wipe"><strong>{{ L.T "public.privacyWipe" }}</strong></label>
|
||||||
<br />
|
<br />
|
||||||
Delete all your subscriptions and related data from our database permanently.
|
{{ L.T "public.privacyWipeHelp" }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<p>
|
<p>
|
||||||
<input type="submit" value="Continue" class="button button-outline" />
|
<input type="submit" value="{{ L.T "globals.buttons.continue" }}" class="button button-outline" />
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
|
@ -59,7 +60,7 @@
|
||||||
if (a == "export") {
|
if (a == "export") {
|
||||||
f.action = "/subscription/export/{{ .Data.SubUUID }}";
|
f.action = "/subscription/export/{{ .Data.SubUUID }}";
|
||||||
return true;
|
return true;
|
||||||
} else if (confirm("Are you sure you want to delete all your subscription data permanently?")) {
|
} else if (confirm("{{ L.T "public.privacyConfirmWipe" }}")) {
|
||||||
f.action = "/subscription/wipe/{{ .Data.SubUUID }}";
|
f.action = "/subscription/wipe/{{ .Data.SubUUID }}";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue