Compare commits
78 Commits
v5.0.1
...
6417f30d77
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6417f30d77 | ||
|
|
12b80699d9 | ||
|
|
581aad4496 | ||
|
|
3bf8bdb410 | ||
|
|
817048985c | ||
|
|
750ce91c69 | ||
|
|
60f7ac9351 | ||
|
|
296245a1b3 | ||
|
|
4c3b58cb90 | ||
|
|
75998ca5f6 | ||
|
|
ea1eb3f857 | ||
|
|
a2bfc0b77e | ||
|
|
b3f60a957f | ||
|
|
425c0d71f4 | ||
|
|
e469296bc8 | ||
|
|
22bcd70f01 | ||
|
|
60c069d73d | ||
|
|
b7e8b1ee1f | ||
|
|
cdf0a5c153 | ||
|
|
827a2081b0 | ||
|
|
e8156e03ce | ||
|
|
c666c4ff9e | ||
|
|
39658c446f | ||
|
|
2085abefb4 | ||
|
|
fb60455d10 | ||
|
|
27f58efae9 | ||
|
|
06275f1907 | ||
|
|
2c5dc61427 | ||
|
|
d661fa874f | ||
|
|
a76099e9e5 | ||
|
|
943a961bfc | ||
|
|
30846f8415 | ||
|
|
e27a3904c6 | ||
|
|
6d99316528 | ||
|
|
d4007d5148 | ||
|
|
deb41f8d1c | ||
|
|
81d05e4dd6 | ||
|
|
fcbebc272b | ||
|
|
e8b0eafc00 | ||
|
|
301c13a60d | ||
|
|
ad66878ea0 | ||
|
|
fbe4c5c9e7 | ||
|
|
ea88b94413 | ||
|
|
7d38890868 | ||
|
|
961116468b | ||
|
|
09d291e119 | ||
|
|
38387d0079 | ||
|
|
eef0021366 | ||
|
|
4a93184c7e | ||
|
|
ad67fc62a6 | ||
|
|
4d74cf4520 | ||
|
|
26c61f8ea1 | ||
|
|
fb39d6183c | ||
|
|
ba24c64fae | ||
|
|
c9c678c04f | ||
|
|
66d7413bfb | ||
|
|
ae84fa37d4 | ||
|
|
6b7e4232c5 | ||
|
|
89b270427b | ||
|
|
98d2ad6e72 | ||
|
|
38c784fe55 | ||
|
|
48643aa93e | ||
|
|
a424f01fc6 | ||
|
|
4f6880d38e | ||
|
|
b71d80e546 | ||
|
|
419f88a9c3 | ||
|
|
aa864fd5bf | ||
|
|
d7e4ecf81c | ||
|
|
a9d3591f5e | ||
|
|
78f3db7f8d | ||
|
|
2081048701 | ||
|
|
d27d2c32cf | ||
|
|
6fc6c1ecea | ||
|
|
d6f5292df7 | ||
|
|
8b8358e913 | ||
|
|
3f3950855a | ||
|
|
d027cb5de2 | ||
|
|
bf502ba766 |
1
.gitignore
vendored
@@ -9,6 +9,7 @@ email-builder/node_modules/
|
||||
email-builder/.cache/
|
||||
email-builder/yarn.lock
|
||||
email-builder/dist/
|
||||
static/public/static/altcha.umd.js
|
||||
.vscode/
|
||||
|
||||
config.toml
|
||||
|
||||
7
Makefile
@@ -33,12 +33,15 @@ FRONTEND_EMAIL_BUILDER_DEPS = \
|
||||
|
||||
BIN := listmonk
|
||||
STATIC := config.toml.sample \
|
||||
schema.sql queries.sql permissions.json \
|
||||
schema.sql queries:/queries permissions.json \
|
||||
static/public:/public \
|
||||
static/email-templates \
|
||||
frontend/dist:/admin \
|
||||
i18n:/i18n
|
||||
|
||||
SQL := $(shell find . -type f -name "*.sql") $(shell find queries -type f -name "*.sql")
|
||||
SRC := $(shell find . -type f -name "*.go")
|
||||
|
||||
.PHONY: build
|
||||
build: $(BIN)
|
||||
|
||||
@@ -54,7 +57,7 @@ $(FRONTEND_EMAIL_BUILDER_YARN_MODULES): frontend/package.json frontend/yarn.lock
|
||||
touch -c $(FRONTEND_EMAIL_BUILDER_YARN_MODULES)
|
||||
|
||||
# Build the backend to ./listmonk.
|
||||
$(BIN): $(shell find . -type f -name "*.go") go.mod go.sum schema.sql queries.sql permissions.json
|
||||
$(BIN): $(SRC) go.mod go.sum schema.sql $(SQL) permissions.json
|
||||
CGO_ENABLED=0 go build -o ${BIN} -ldflags="-s -w -X 'main.buildString=${BUILDSTR}' -X 'main.versionString=${VERSION}'" cmd/*.go
|
||||
|
||||
# Run the backend in dev mode. The frontend assets in dev mode are loaded from disk from frontend/dist.
|
||||
|
||||
23
cmd/admin.go
@@ -7,6 +7,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/knadh/listmonk/internal/captcha"
|
||||
"github.com/labstack/echo/v4"
|
||||
null "gopkg.in/volatiletech/null.v6"
|
||||
)
|
||||
@@ -15,10 +16,13 @@ type serverConfig struct {
|
||||
RootURL string `json:"root_url"`
|
||||
FromEmail string `json:"from_email"`
|
||||
PublicSubscription struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
CaptchaEnabled bool `json:"captcha_enabled"`
|
||||
CaptchaKey null.String `json:"captcha_key"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CaptchaEnabled bool `json:"captcha_enabled"`
|
||||
CaptchaProvider null.String `json:"captcha_provider"`
|
||||
CaptchaKey null.String `json:"captcha_key"`
|
||||
AltchaComplexity int `json:"altcha_complexity"`
|
||||
} `json:"public_subscription"`
|
||||
MediaProvider string `json:"media_provider"`
|
||||
Messengers []string `json:"messengers"`
|
||||
Langs []i18nLang `json:"langs"`
|
||||
Lang string `json:"lang"`
|
||||
@@ -39,11 +43,20 @@ func (a *App) GetServerConfig(c echo.Context) error {
|
||||
HasLegacyUser: a.cfg.HasLegacyUser,
|
||||
}
|
||||
out.PublicSubscription.Enabled = a.cfg.EnablePublicSubPage
|
||||
if a.cfg.Security.EnableCaptcha {
|
||||
|
||||
// CAPTCHA.
|
||||
if a.cfg.Security.Captcha.Altcha.Enabled {
|
||||
out.PublicSubscription.CaptchaEnabled = true
|
||||
out.PublicSubscription.CaptchaKey = null.StringFrom(a.cfg.Security.CaptchaKey)
|
||||
out.PublicSubscription.CaptchaProvider = null.StringFrom(captcha.ProviderAltcha)
|
||||
out.PublicSubscription.AltchaComplexity = a.cfg.Security.Captcha.Altcha.Complexity
|
||||
} else if a.cfg.Security.Captcha.HCaptcha.Enabled {
|
||||
out.PublicSubscription.CaptchaEnabled = true
|
||||
out.PublicSubscription.CaptchaProvider = null.StringFrom(captcha.ProviderHCaptcha)
|
||||
out.PublicSubscription.CaptchaKey = null.StringFrom(a.cfg.Security.Captcha.HCaptcha.Key)
|
||||
}
|
||||
|
||||
out.MediaProvider = a.cfg.MediaUpload.Provider
|
||||
|
||||
// Language list.
|
||||
langList, err := getI18nLangList(a.fs)
|
||||
if err != nil {
|
||||
|
||||
435
cmd/auth.go
@@ -1,9 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
@@ -11,12 +14,25 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/knadh/listmonk/internal/auth"
|
||||
"github.com/knadh/listmonk/internal/i18n"
|
||||
"github.com/knadh/listmonk/internal/notifs"
|
||||
"github.com/knadh/listmonk/internal/tmptokens"
|
||||
"github.com/knadh/listmonk/internal/utils"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/zerodha/simplesessions/v3"
|
||||
"gopkg.in/volatiletech/null.v6"
|
||||
)
|
||||
|
||||
const (
|
||||
passwordResetTTL = 30 * time.Minute
|
||||
twofaTokenTTL = 5 * time.Minute
|
||||
|
||||
// Length of reset and 2FA auth tokens.
|
||||
tmpAuthTokenLen = 64
|
||||
)
|
||||
|
||||
type loginTpl struct {
|
||||
Title string
|
||||
Description string
|
||||
@@ -34,13 +50,37 @@ type oidcState struct {
|
||||
Next string `json:"next"`
|
||||
}
|
||||
|
||||
var oidcProviders = map[string]bool{
|
||||
"google.com": true,
|
||||
"microsoftonline.com": true,
|
||||
"auth0.com": true,
|
||||
"github.com": true,
|
||||
type forgotPasswordTpl struct {
|
||||
Title string
|
||||
Description string
|
||||
Error string
|
||||
}
|
||||
|
||||
type resetPasswordTpl struct {
|
||||
Title string
|
||||
Description string
|
||||
Token string
|
||||
Email string
|
||||
Error string
|
||||
}
|
||||
|
||||
type twofaTpl struct {
|
||||
Title string
|
||||
Description string
|
||||
Token string
|
||||
NextURI string
|
||||
Error string
|
||||
}
|
||||
|
||||
var (
|
||||
oidcProviders = map[string]struct{}{
|
||||
"google.com": {},
|
||||
"microsoftonline.com": {},
|
||||
"auth0.com": {},
|
||||
"github.com": {},
|
||||
}
|
||||
)
|
||||
|
||||
// LoginPage renders the login page and handles the login form.
|
||||
func (a *App) LoginPage(c echo.Context) error {
|
||||
// Has the user been setup?
|
||||
@@ -83,6 +123,47 @@ func (a *App) LoginSetupPage(c echo.Context) error {
|
||||
return a.renderLoginSetupPage(c, loginErr)
|
||||
}
|
||||
|
||||
// TwofaPage renders the 2FA verification page and handles the 2FA form submission.
|
||||
func (a *App) TwofaPage(c echo.Context) error {
|
||||
var token, next string
|
||||
|
||||
if c.Request().Method == http.MethodPost {
|
||||
token = strings.TrimSpace(c.FormValue("token"))
|
||||
next = utils.SanitizeURI(c.FormValue("next"))
|
||||
} else {
|
||||
token = strings.TrimSpace(c.QueryParam("token"))
|
||||
next = utils.SanitizeURI(c.QueryParam("next"))
|
||||
}
|
||||
|
||||
// If there's no token, redirect.
|
||||
if len(token) < tmpAuthTokenLen {
|
||||
return c.Redirect(http.StatusFound, uriAdmin)
|
||||
}
|
||||
|
||||
if next == "" || next == "/" {
|
||||
next = uriAdmin
|
||||
}
|
||||
|
||||
// Validate the 2FA temp token.
|
||||
data, err := tmptokens.Check(token)
|
||||
if err != nil {
|
||||
return c.Redirect(http.StatusFound, uriAdmin)
|
||||
}
|
||||
|
||||
userID, ok := data.(int)
|
||||
if !ok {
|
||||
return a.renderTwofaPage(c, token, next, a.i18n.T("users.invalidRequest"))
|
||||
}
|
||||
|
||||
// Process the 2FA verification POST request.
|
||||
if c.Request().Method == http.MethodPost {
|
||||
return a.doTwofaVerify(c, token, userID, next)
|
||||
}
|
||||
|
||||
// Render the 2FA verification page.
|
||||
return a.renderTwofaPage(c, token, next, "")
|
||||
}
|
||||
|
||||
// Logout logs a user out.
|
||||
func (a *App) Logout(c echo.Context) error {
|
||||
// Delete the session from the DB and cookie.
|
||||
@@ -158,11 +239,22 @@ func (a *App) OIDCFinish(c echo.Context) error {
|
||||
return a.renderLoginPage(c, err)
|
||||
}
|
||||
email = strings.ToLower(em.Address)
|
||||
claims.Email = email
|
||||
|
||||
// Get the user by e-mail received from OIDC.
|
||||
user, err := a.core.GetUser(0, "", email)
|
||||
if err != nil {
|
||||
return a.renderLoginPage(c, err)
|
||||
user, userErr := a.core.GetUser(0, "", email)
|
||||
if userErr != nil {
|
||||
// If the user doesn't exist, and auto-creation is enabled, create a new user.
|
||||
if httpErr, ok := userErr.(*echo.HTTPError); ok && httpErr.Code == http.StatusNotFound && a.cfg.Security.OIDC.AutoCreateUsers {
|
||||
u, err := a.createOIDCUser(claims, c)
|
||||
if err != nil {
|
||||
return a.renderLoginPage(c, err)
|
||||
}
|
||||
user = u
|
||||
userErr = nil
|
||||
} else {
|
||||
return a.renderLoginPage(c, userErr)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the user login state (avatar, logged in date) in the DB.
|
||||
@@ -179,6 +271,51 @@ func (a *App) OIDCFinish(c echo.Context) error {
|
||||
return c.Redirect(http.StatusFound, utils.SanitizeURI(state.Next))
|
||||
}
|
||||
|
||||
// ForgotPage renders the forgot password page and handles the forgot password form.
|
||||
func (a *App) ForgotPage(c echo.Context) error {
|
||||
// Process the forgot password request.
|
||||
if c.Request().Method == http.MethodPost {
|
||||
return a.doForgotPassword(c)
|
||||
}
|
||||
|
||||
// Render the forgot page.
|
||||
out := forgotPasswordTpl{Title: a.i18n.T("users.forgotPassword")}
|
||||
return c.Render(http.StatusOK, "admin-forgot-password", out)
|
||||
}
|
||||
|
||||
// ResetPage renders the reset password page and handles the reset password form.
|
||||
func (a *App) ResetPage(c echo.Context) error {
|
||||
var (
|
||||
token = strings.TrimSpace(c.QueryParam("token"))
|
||||
email = strings.ToLower(strings.TrimSpace(c.QueryParam("email")))
|
||||
)
|
||||
|
||||
// Validate token and email (don't delete it yet, as we may need it for POST).
|
||||
data, err := tmptokens.Check(email)
|
||||
if err != nil {
|
||||
return c.Render(http.StatusBadRequest, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.invalidResetLink")))
|
||||
}
|
||||
|
||||
tk, ok := data.(string)
|
||||
if !ok || tk != token {
|
||||
return c.Render(http.StatusBadRequest, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.invalidResetLink")))
|
||||
}
|
||||
|
||||
// Validate that the user exists.
|
||||
_, err = a.core.GetUser(0, "", email)
|
||||
if err != nil {
|
||||
return c.Render(http.StatusBadRequest, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.invalidResetLink")))
|
||||
}
|
||||
|
||||
// Process the reset password request form with the new passwords.
|
||||
if c.Request().Method == http.MethodPost {
|
||||
return a.doResetPassword(c, token, email)
|
||||
}
|
||||
|
||||
// Render the reset password form for GET request.
|
||||
return a.renderResetPasswordPage(c, token, email, "")
|
||||
}
|
||||
|
||||
// renderLoginPage renders the login page and handles the login form.
|
||||
func (a *App) renderLoginPage(c echo.Context, loginErr error) error {
|
||||
next := utils.SanitizeURI(c.FormValue("next"))
|
||||
@@ -279,13 +416,51 @@ func (a *App) renderLoginSetupPage(c echo.Context, loginErr error) error {
|
||||
return c.Render(http.StatusOK, "admin-login-setup", out)
|
||||
}
|
||||
|
||||
// createOIDCUser creates a new user in the DB with the OIDC claims.
|
||||
func (a *App) createOIDCUser(claims auth.OIDCclaim, c echo.Context) (auth.User, error) {
|
||||
name := claims.Name
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(claims.PreferredUsername)
|
||||
}
|
||||
if name == "" {
|
||||
name = strings.Split(claims.Email, "@")[0]
|
||||
}
|
||||
|
||||
var listRoleID *int
|
||||
if a.cfg.Security.OIDC.DefaultListRoleID > 0 {
|
||||
listRoleID = &a.cfg.Security.OIDC.DefaultListRoleID
|
||||
}
|
||||
|
||||
user, err := a.core.CreateUser(auth.User{
|
||||
Type: auth.UserTypeUser,
|
||||
HasPassword: false,
|
||||
PasswordLogin: false,
|
||||
Username: claims.Email,
|
||||
Name: name,
|
||||
Email: null.NewString(claims.Email, true),
|
||||
UserRoleID: a.cfg.Security.OIDC.DefaultUserRoleID,
|
||||
ListRoleID: listRoleID,
|
||||
Status: auth.UserStatusEnabled,
|
||||
})
|
||||
|
||||
return user, err
|
||||
}
|
||||
|
||||
// doLogin logs a user in with a username and password.
|
||||
func (a *App) doLogin(c echo.Context) error {
|
||||
var (
|
||||
username = strings.TrimSpace(c.FormValue("username"))
|
||||
password = strings.TrimSpace(c.FormValue("password"))
|
||||
startTime = time.Now()
|
||||
username = strings.TrimSpace(c.FormValue("username"))
|
||||
password = strings.TrimSpace(c.FormValue("password"))
|
||||
)
|
||||
|
||||
// Ensure timing mitigation is applied regardless of early returns
|
||||
defer func() {
|
||||
if elapsed := time.Since(startTime).Milliseconds(); elapsed < 100 {
|
||||
time.Sleep(time.Duration(100-elapsed) * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
if !strHasLen(username, 3, stdInputMaxLen) {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.Ts("globals.messages.invalidFields", "name", "username"))
|
||||
}
|
||||
@@ -299,9 +474,21 @@ func (a *App) doLogin(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Resist potential constant-time-comparison attacks with a min response time.
|
||||
if ms := time.Since(time.Now()).Milliseconds(); ms < 100 {
|
||||
time.Sleep(time.Duration(ms))
|
||||
// If TOTP is enabled for the user, create a temp token and redirect to the 2FA page.
|
||||
if user.TwofaType == models.TwofaTypeTOTP {
|
||||
// Generate a random token.
|
||||
token, err := generateRandomString(tmpAuthTokenLen)
|
||||
if err != nil {
|
||||
a.log.Printf("error generating 2FA token: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("globals.messages.internalError"))
|
||||
}
|
||||
|
||||
// Set the token.
|
||||
tmptokens.Set(token, twofaTokenTTL, user.ID)
|
||||
|
||||
// Redirect to 2FA page.
|
||||
next := utils.SanitizeURI(c.FormValue("next"))
|
||||
return c.Redirect(http.StatusFound, fmt.Sprintf("%s/login/twofa?token=%s&next=%s", uriAdmin, token, url.QueryEscape(next)))
|
||||
}
|
||||
|
||||
// Set the session in the DB and cookie.
|
||||
@@ -378,3 +565,225 @@ func (a *App) doFirstTimeSetup(c echo.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// renderResetPasswordPage renders the reset password page.
|
||||
func (a *App) renderResetPasswordPage(c echo.Context, token, email, errMsg string) error {
|
||||
out := resetPasswordTpl{
|
||||
Title: a.i18n.T("users.resetPassword"),
|
||||
Token: token,
|
||||
Email: email,
|
||||
Error: errMsg,
|
||||
}
|
||||
return c.Render(http.StatusOK, "admin-reset-password", out)
|
||||
}
|
||||
|
||||
// doForgotPassword handles the forgot password form submission.
|
||||
func (a *App) doForgotPassword(c echo.Context) error {
|
||||
var (
|
||||
email = strings.ToLower(strings.TrimSpace(c.FormValue("email")))
|
||||
)
|
||||
|
||||
// Validate email format.
|
||||
if !utils.ValidateEmail(email) {
|
||||
return c.Render(http.StatusOK, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.resetLinkSent")))
|
||||
}
|
||||
|
||||
// Get the user by email.
|
||||
user, err := a.core.GetUser(0, "", email)
|
||||
if err != nil {
|
||||
return c.Render(http.StatusOK, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.resetLinkSent")))
|
||||
}
|
||||
|
||||
// If the password login is disabled, do not proceed, but show success message to prevent email enumeration.
|
||||
if !user.PasswordLogin {
|
||||
return c.Render(http.StatusOK, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.resetLinkSent")))
|
||||
}
|
||||
|
||||
// Generate a random token.
|
||||
token, err := generateRandomString(tmpAuthTokenLen)
|
||||
if err != nil {
|
||||
a.log.Printf("error generating reset token: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("globals.messages.internalError"))
|
||||
}
|
||||
|
||||
// Store the reset token in tmptokens.
|
||||
tmptokens.Set(email, passwordResetTTL, token)
|
||||
|
||||
// Prepare the reset URL.
|
||||
resetURL := fmt.Sprintf("%s/admin/reset?token=%s&email=%s", a.urlCfg.RootURL, token, url.QueryEscape(email))
|
||||
|
||||
// Prepare the email.
|
||||
var msg bytes.Buffer
|
||||
data := struct {
|
||||
ResetURL string
|
||||
L *i18n.I18n
|
||||
}{
|
||||
ResetURL: resetURL,
|
||||
L: a.i18n,
|
||||
}
|
||||
|
||||
// Render the email template.
|
||||
if err := notifs.Tpls.ExecuteTemplate(&msg, notifs.TplForgotPassword, data); err != nil {
|
||||
a.log.Printf("error compiling notification template '%s': %v", notifs.TplForgotPassword, err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("globals.messages.internalError"))
|
||||
}
|
||||
|
||||
subject, body := notifs.GetTplSubject(a.i18n.T("email.forgotPassword.subject"), msg.Bytes())
|
||||
|
||||
// Send the email.
|
||||
if err := a.emailMsgr.Push(models.Message{
|
||||
From: a.cfg.FromEmail,
|
||||
To: []string{email},
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
}); err != nil {
|
||||
a.log.Printf("error sending reset email: %s", err)
|
||||
}
|
||||
|
||||
// Show the success e-mail nonetheless to prevent e-mail enumeration.
|
||||
return c.Render(http.StatusOK, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.resetLinkSent")))
|
||||
}
|
||||
|
||||
// doResetPassword handles the reset password form submission.
|
||||
func (a *App) doResetPassword(c echo.Context, token, email string) error {
|
||||
var (
|
||||
password = c.FormValue("password")
|
||||
password2 = c.FormValue("password2")
|
||||
)
|
||||
|
||||
// Validate password.
|
||||
if !strHasLen(password, 8, stdInputMaxLen) {
|
||||
return a.renderResetPasswordPage(c, token, email, a.i18n.Ts("globals.messages.invalidFields", "name", "password"))
|
||||
}
|
||||
if password != password2 {
|
||||
return a.renderResetPasswordPage(c, token, email, a.i18n.T("users.passwordMismatch"))
|
||||
}
|
||||
|
||||
// Validate and consume the token (this deletes it).
|
||||
data, err := tmptokens.Get(email)
|
||||
if err != nil {
|
||||
return c.Render(http.StatusBadRequest, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.invalidResetLink")))
|
||||
}
|
||||
|
||||
tk, ok := data.(string)
|
||||
if !ok || tk != token {
|
||||
return c.Render(http.StatusBadRequest, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.invalidResetLink")))
|
||||
}
|
||||
|
||||
// Get the user.
|
||||
user, err := a.core.GetUser(0, "", email)
|
||||
if err != nil {
|
||||
return c.Render(http.StatusBadRequest, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("users.invalidResetLink")))
|
||||
}
|
||||
|
||||
// Password login is disabled for the user.
|
||||
if !user.PasswordLogin {
|
||||
return c.Render(http.StatusBadRequest, tplMessage, makeMsgTpl(a.i18n.T("users.resetPassword"), "", a.i18n.T("public.invalidFeature")))
|
||||
}
|
||||
|
||||
user.Password = null.NewString(password, true)
|
||||
if _, err := a.core.UpdateUserProfile(user.ID, user); err != nil {
|
||||
a.log.Printf("error updating user password: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("globals.messages.internalError"))
|
||||
}
|
||||
|
||||
// Log the user in directly without forcing a manual login right after password change.
|
||||
if err := a.auth.SaveSession(user, "", c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Redirect to the admin page.
|
||||
return c.Redirect(http.StatusFound, uriAdmin)
|
||||
}
|
||||
|
||||
// renderTwofaPage renders the 2FA verification page.
|
||||
func (a *App) renderTwofaPage(c echo.Context, token, next, errMsg string) error {
|
||||
out := twofaTpl{
|
||||
Title: a.i18n.T("users.twoFA"),
|
||||
Description: "",
|
||||
Token: token,
|
||||
NextURI: next,
|
||||
Error: errMsg,
|
||||
}
|
||||
return c.Render(http.StatusOK, "admin-twofa", out)
|
||||
}
|
||||
|
||||
// doTwofaVerify handles the 2FA verification form submission.
|
||||
func (a *App) doTwofaVerify(c echo.Context, token string, userID int, next string) error {
|
||||
totpCode := strings.TrimSpace(c.FormValue("totp_code"))
|
||||
|
||||
// Validate.
|
||||
if !strHasLen(totpCode, 6, 6) {
|
||||
return a.renderTwofaPage(c, token, next, a.i18n.T("globals.messages.invalidValue"))
|
||||
}
|
||||
|
||||
// Get the user.
|
||||
user, err := a.core.GetUser(userID, "", "")
|
||||
if err != nil {
|
||||
return a.renderTwofaPage(c, token, next, a.i18n.T("users.invalidRequest"))
|
||||
}
|
||||
|
||||
// Verify that TOTP is actually enabled for the user.
|
||||
if user.TwofaType != models.TwofaTypeTOTP {
|
||||
return a.renderTwofaPage(c, token, next, a.i18n.T("users.twoFANotEnabled"))
|
||||
}
|
||||
|
||||
// Verify the TOTP code.
|
||||
valid := totp.Validate(totpCode, user.TwofaKey.String)
|
||||
if !valid {
|
||||
return a.renderTwofaPage(c, token, next, a.i18n.T("globals.messages.invalidValue"))
|
||||
}
|
||||
|
||||
// Invalidate the token.
|
||||
tmptokens.Delete(token)
|
||||
|
||||
// Set the session.
|
||||
if err := a.auth.SaveSession(user, "", c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Redirect to the next page.
|
||||
return c.Redirect(http.StatusFound, next)
|
||||
}
|
||||
|
||||
// GenerateTOTPQR generates a TOTP QR code for a user to scan with their authenticator app.
|
||||
func (a *App) GenerateTOTPQR(c echo.Context) error {
|
||||
u := c.Get(auth.UserHTTPCtxKey).(auth.User)
|
||||
|
||||
// If TOTP is already enabled, don't generate a new key.
|
||||
if u.TwofaType == models.TwofaTypeTOTP {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("users.twoFAAlreadyEnabled"))
|
||||
}
|
||||
|
||||
// Generate a new TOTP key.
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: a.cfg.SiteName,
|
||||
AccountName: u.Email.String,
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Printf("error generating TOTP key: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("globals.messages.internalError"))
|
||||
}
|
||||
|
||||
// Convert the TOTP key to a QR code image.
|
||||
img, err := key.Image(200, 200)
|
||||
if err != nil {
|
||||
a.log.Printf("error generating QR code: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("globals.messages.internalError"))
|
||||
}
|
||||
|
||||
// Encode the QR code as a PNG and return it as base64.
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, img); err != nil {
|
||||
a.log.Printf("error encoding QR code: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("globals.messages.internalError"))
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{struct {
|
||||
Secret string `json:"secret"`
|
||||
QR string `json:"qr"`
|
||||
}{
|
||||
Secret: key.Secret(),
|
||||
QR: base64.StdEncoding.EncodeToString(buf.Bytes()),
|
||||
}})
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func (a *App) DeleteBounces(c echo.Context) error {
|
||||
}
|
||||
|
||||
// Delete bounces from the DB.
|
||||
if err := a.core.DeleteBounces(ids); err != nil {
|
||||
if err := a.core.DeleteBounces(ids, all); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -97,7 +97,16 @@ func (a *App) DeleteBounces(c echo.Context) error {
|
||||
func (a *App) DeleteBounce(c echo.Context) error {
|
||||
// Delete bounces from the DB.
|
||||
id := getID(c)
|
||||
if err := a.core.DeleteBounces([]int{id}); err != nil {
|
||||
if err := a.core.DeleteBounces([]int{id}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{true})
|
||||
}
|
||||
|
||||
// BlocklistBouncedSubscribers handles blocklisting of all bounced subscribers.
|
||||
func (a *App) BlocklistBouncedSubscribers(c echo.Context) error {
|
||||
if err := a.core.BlocklistBouncedSubscribers(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,14 @@ func initHTTPHandlers(e *echo.Echo, a *App) {
|
||||
e.DefaultHTTPErrorHandler(err, c)
|
||||
}
|
||||
|
||||
// Configure CORS middleware if domains are configured.
|
||||
if len(a.cfg.Security.CorsOrigins) > 0 {
|
||||
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||
AllowOrigins: a.cfg.Security.CorsOrigins,
|
||||
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
|
||||
}))
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Authenticated non /api handlers.
|
||||
{
|
||||
@@ -107,6 +115,7 @@ func initHTTPHandlers(e *echo.Echo, a *App) {
|
||||
|
||||
g.GET("/api/subscribers", pm(a.QuerySubscribers, "subscribers:get_all", "subscribers:get"))
|
||||
g.GET("/api/subscribers/:id", pm(hasID(a.GetSubscriber), "subscribers:get_all", "subscribers:get"))
|
||||
g.GET("/api/subscribers/:id/activity", pm(hasID(a.GetSubscriberActivity), "subscribers:get_all", "subscribers:get"))
|
||||
g.GET("/api/subscribers/:id/export", pm(hasID(a.ExportSubscriberData), "subscribers:get_all", "subscribers:get"))
|
||||
g.GET("/api/subscribers/:id/bounces", pm(hasID(a.GetSubscriberBounces), "bounces:get"))
|
||||
g.DELETE("/api/subscribers/:id/bounces", pm(hasID(a.DeleteSubscriberBounces), "bounces:manage"))
|
||||
@@ -121,6 +130,7 @@ func initHTTPHandlers(e *echo.Echo, a *App) {
|
||||
g.DELETE("/api/subscribers", pm(a.DeleteSubscribers, "subscribers:manage"))
|
||||
|
||||
g.GET("/api/bounces", pm(a.GetBounces, "bounces:get"))
|
||||
g.PUT("/api/bounces/blocklist", pm(a.BlocklistBouncedSubscribers, "bounces:manage"))
|
||||
g.GET("/api/bounces/:id", pm(hasID(a.GetBounce), "bounces:get"))
|
||||
g.DELETE("/api/bounces", pm(a.DeleteBounces, "bounces:manage"))
|
||||
g.DELETE("/api/bounces/:id", pm(hasID(a.DeleteBounce), "bounces:manage"))
|
||||
@@ -191,6 +201,11 @@ func initHTTPHandlers(e *echo.Echo, a *App) {
|
||||
g.DELETE("/api/users/:id", pm(hasID(a.DeleteUser), "users:manage"))
|
||||
g.POST("/api/logout", a.Logout)
|
||||
|
||||
// TOTP 2FA endpoints
|
||||
g.GET("/api/users/:id/twofa/totp", hasID(a.GenerateTOTPQR))
|
||||
g.PUT("/api/users/:id/twofa", hasID(a.EnableTOTP))
|
||||
g.DELETE("/api/users/:id/twofa", hasID(a.DisableTOTP))
|
||||
|
||||
g.GET("/api/roles/users", pm(a.GetUserRoles, "roles:get"))
|
||||
g.GET("/api/roles/lists", pm(a.GeListRoles, "roles:get"))
|
||||
g.POST("/api/roles/users", pm(a.CreateUserRole, "roles:manage"))
|
||||
@@ -221,9 +236,15 @@ func initHTTPHandlers(e *echo.Echo, a *App) {
|
||||
return c.Render(http.StatusOK, "home", publicTpl{Title: "listmonk"})
|
||||
})
|
||||
|
||||
// Public admin endpoints (login page, OIDC endpoints).
|
||||
// Public admin endpoints (login page, OIDC endpoints, password reset).
|
||||
g.GET(path.Join(uriAdmin, "/login"), a.LoginPage)
|
||||
g.POST(path.Join(uriAdmin, "/login"), a.LoginPage)
|
||||
g.GET(path.Join(uriAdmin, "/login/twofa"), a.TwofaPage)
|
||||
g.POST(path.Join(uriAdmin, "/login/twofa"), a.TwofaPage)
|
||||
g.GET(path.Join(uriAdmin, "/forgot"), a.ForgotPage)
|
||||
g.POST(path.Join(uriAdmin, "/forgot"), a.ForgotPage)
|
||||
g.GET(path.Join(uriAdmin, "/reset"), a.ResetPage)
|
||||
g.POST(path.Join(uriAdmin, "/reset"), a.ResetPage)
|
||||
|
||||
if a.cfg.Security.OIDC.Enabled {
|
||||
g.POST("/auth/oidc", a.OIDCLogin)
|
||||
@@ -233,6 +254,7 @@ func initHTTPHandlers(e *echo.Echo, a *App) {
|
||||
// Public APIs.
|
||||
g.GET("/api/public/lists", a.GetPublicLists)
|
||||
g.POST("/api/public/subscription", a.PublicSubscription)
|
||||
g.GET("/api/public/captcha/altcha", a.AltchaChallenge)
|
||||
if a.cfg.EnablePublicArchive {
|
||||
g.GET("/api/public/archive", a.GetCampaignArchives)
|
||||
}
|
||||
|
||||
103
cmd/init.go
@@ -54,8 +54,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
queryFilePath = "queries.sql"
|
||||
emailMsgr = "email"
|
||||
// Path to the SQL queries directory in the embedded FS.
|
||||
queryFilePath = "/queries"
|
||||
|
||||
emailMsgr = "email"
|
||||
)
|
||||
|
||||
// UrlConfig contains various URL constants used in the app.
|
||||
@@ -96,16 +98,29 @@ type Config struct {
|
||||
} `koanf:"privacy"`
|
||||
Security struct {
|
||||
OIDC struct {
|
||||
Enabled bool `koanf:"enabled"`
|
||||
ProviderURL string `koanf:"provider_url"`
|
||||
ProviderName string `koanf:"provider_name"`
|
||||
ClientID string `koanf:"client_id"`
|
||||
ClientSecret string `koanf:"client_secret"`
|
||||
Enabled bool `koanf:"enabled"`
|
||||
ProviderURL string `koanf:"provider_url"`
|
||||
ProviderName string `koanf:"provider_name"`
|
||||
ClientID string `koanf:"client_id"`
|
||||
ClientSecret string `koanf:"client_secret"`
|
||||
AutoCreateUsers bool `koanf:"auto_create_users"`
|
||||
DefaultUserRoleID int `koanf:"default_user_role_id"`
|
||||
DefaultListRoleID int `koanf:"default_list_role_id"`
|
||||
} `koanf:"oidc"`
|
||||
|
||||
EnableCaptcha bool `koanf:"enable_captcha"`
|
||||
CaptchaKey string `koanf:"captcha_key"`
|
||||
CaptchaSecret string `koanf:"captcha_secret"`
|
||||
Captcha struct {
|
||||
Altcha struct {
|
||||
Enabled bool `koanf:"enabled"`
|
||||
Complexity int `koanf:"complexity"`
|
||||
} `koanf:"altcha"`
|
||||
HCaptcha struct {
|
||||
Enabled bool `koanf:"enabled"`
|
||||
Key string `koanf:"key"`
|
||||
Secret string `koanf:"secret"`
|
||||
} `koanf:"hcaptcha"`
|
||||
} `koanf:"captcha"`
|
||||
|
||||
CorsOrigins []string `koanf:"cors_origins"`
|
||||
} `koanf:"security"`
|
||||
|
||||
Appearance struct {
|
||||
@@ -186,7 +201,7 @@ func initFS(appDir, frontendDir, staticDir, i18nDir string) stuffbin.FileSystem
|
||||
// These paths are joined with appDir.
|
||||
appFiles = []string{
|
||||
"./config.toml.sample:config.toml.sample",
|
||||
"./queries.sql:queries.sql",
|
||||
"./queries:queries",
|
||||
"./schema.sql:schema.sql",
|
||||
"./permissions.json:permissions.json",
|
||||
}
|
||||
@@ -320,19 +335,35 @@ func initDB() *sqlx.DB {
|
||||
return db.Unsafe()
|
||||
}
|
||||
|
||||
// readQueries reads named SQL queries from the SQL queries file into a query map.
|
||||
func readQueries(sqlFile string, fs stuffbin.FileSystem) goyesql.Queries {
|
||||
// Load SQL queries.
|
||||
qB, err := fs.Read(sqlFile)
|
||||
func readQueries(dir string, fs stuffbin.FileSystem) goyesql.Queries {
|
||||
out := goyesql.Queries{}
|
||||
|
||||
// Glob all the .sql files in the queries directory.
|
||||
qPath := path.Join(dir, "/*.sql")
|
||||
files, err := fs.Glob(qPath)
|
||||
if err != nil {
|
||||
lo.Fatalf("error reading SQL file %s: %v", sqlFile, err)
|
||||
}
|
||||
qMap, err := goyesql.ParseBytes(qB)
|
||||
if err != nil {
|
||||
lo.Fatalf("error parsing SQL queries: %v", err)
|
||||
lo.Fatalf("error reading *.sql query files from %s: %v", qPath, err)
|
||||
}
|
||||
|
||||
return qMap
|
||||
// Read and merge queries from all files into one map.
|
||||
for _, file := range files {
|
||||
// Read the SQL file.
|
||||
b, err := fs.Read(file)
|
||||
if err != nil {
|
||||
lo.Fatalf("error reading SQL file %s: %v", file, err)
|
||||
}
|
||||
|
||||
// Parse queries in it into a map.
|
||||
mp, err := goyesql.ParseBytes(b)
|
||||
if err != nil {
|
||||
lo.Fatalf("error parsing SQL queries: %v", err)
|
||||
}
|
||||
|
||||
// Merge into the main query map.
|
||||
maps.Copy(out, mp)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// prepareQueries queries prepares a query map and returns a *Queries
|
||||
@@ -902,9 +933,12 @@ func initHTTPServer(cfg *Config, urlCfg *UrlConfig, i *i18n.I18n, fs stuffbin.Fi
|
||||
|
||||
// initCaptcha initializes the captcha service.
|
||||
func initCaptcha() *captcha.Captcha {
|
||||
return captcha.New(captcha.Opt{
|
||||
CaptchaSecret: ko.String("security.captcha_secret"),
|
||||
})
|
||||
var opt captcha.Opt
|
||||
if err := ko.Unmarshal("security.captcha", &opt); err != nil {
|
||||
lo.Fatalf("error loading captcha config: %v", err)
|
||||
}
|
||||
|
||||
return captcha.New(opt)
|
||||
}
|
||||
|
||||
// initCron initializes the cron job for refreshing slow query cache.
|
||||
@@ -988,7 +1022,12 @@ func initTplFuncs(i *i18n.I18n, u *UrlConfig) template.FuncMap {
|
||||
}
|
||||
|
||||
// Copy spring functions.
|
||||
maps.Copy(funcs, sprig.GenericFuncMap())
|
||||
sprigFuncs := sprig.GenericFuncMap()
|
||||
delete(sprigFuncs, "env")
|
||||
delete(sprigFuncs, "expandenv")
|
||||
delete(sprigFuncs, "getHostByName")
|
||||
|
||||
maps.Copy(funcs, sprigFuncs)
|
||||
|
||||
return funcs
|
||||
}
|
||||
@@ -1000,11 +1039,14 @@ func initAuth(co *core.Core, db *sql.DB, ko *koanf.Koanf) (bool, *auth.Auth) {
|
||||
// If OIDC is enabled, set up the OIDC config.
|
||||
if ko.Bool("security.oidc.enabled") {
|
||||
oidcCfg = auth.OIDCConfig{
|
||||
Enabled: true,
|
||||
ProviderURL: ko.String("security.oidc.provider_url"),
|
||||
ClientID: ko.String("security.oidc.client_id"),
|
||||
ClientSecret: ko.String("security.oidc.client_secret"),
|
||||
RedirectURL: fmt.Sprintf("%s/auth/oidc", strings.TrimRight(ko.String("app.root_url"), "/")),
|
||||
Enabled: true,
|
||||
ProviderURL: ko.String("security.oidc.provider_url"),
|
||||
ClientID: ko.String("security.oidc.client_id"),
|
||||
ClientSecret: ko.String("security.oidc.client_secret"),
|
||||
AutoCreateUsers: ko.Bool("security.oidc.auto_create_users"),
|
||||
DefaultUserRoleID: ko.Int("security.oidc.default_user_role_id"),
|
||||
DefaultListRoleID: ko.Int("security.oidc.default_list_role_id"),
|
||||
RedirectURL: fmt.Sprintf("%s/auth/oidc", strings.TrimRight(ko.String("app.root_url"), "/")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,6 +1059,7 @@ func initAuth(co *core.Core, db *sql.DB, ko *koanf.Koanf) (bool, *auth.Auth) {
|
||||
},
|
||||
SetCookie: func(cookie *http.Cookie, w any) error {
|
||||
c := w.(echo.Context)
|
||||
cookie.SameSite = http.SameSiteLaxMode
|
||||
c.SetCookie(cookie)
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -181,7 +181,7 @@ func main() {
|
||||
// Initialize the media store.
|
||||
media = initMediaStore(ko)
|
||||
|
||||
fbOptinNotify = makeOptinNotifyHook(ko.Bool("app.send_optin_confirmation"), urlCfg, queries, i18n)
|
||||
fbOptinNotify = makeOptinNotifyHook(ko.Bool("privacy.unsubscribe_header"), urlCfg, queries, i18n)
|
||||
|
||||
// Crud core.
|
||||
core = initCore(fbOptinNotify, queries, db, i18n, ko)
|
||||
|
||||
131
cmd/public.go
@@ -12,6 +12,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/knadh/listmonk/internal/captcha"
|
||||
"github.com/knadh/listmonk/internal/i18n"
|
||||
"github.com/knadh/listmonk/internal/manager"
|
||||
"github.com/knadh/listmonk/internal/notifs"
|
||||
@@ -88,8 +89,13 @@ type msgTpl struct {
|
||||
|
||||
type subFormTpl struct {
|
||||
publicTpl
|
||||
Lists []models.List
|
||||
CaptchaKey string
|
||||
Lists []models.List
|
||||
Captcha struct {
|
||||
Enabled bool
|
||||
Provider string
|
||||
Key string
|
||||
Complexity int
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -427,9 +433,15 @@ func (a *App) SubscriptionFormPage(c echo.Context) error {
|
||||
out.Title = a.i18n.T("public.sub")
|
||||
out.Lists = lists
|
||||
|
||||
// Captcha is enabled. Set the key for the template to render.
|
||||
if a.cfg.Security.EnableCaptcha {
|
||||
out.CaptchaKey = a.cfg.Security.CaptchaKey
|
||||
// Captcha configuration for template rendering.
|
||||
if a.cfg.Security.Captcha.Altcha.Enabled {
|
||||
out.Captcha.Enabled = true
|
||||
out.Captcha.Provider = "altcha"
|
||||
out.Captcha.Complexity = a.cfg.Security.Captcha.Altcha.Complexity
|
||||
} else if a.cfg.Security.Captcha.HCaptcha.Enabled {
|
||||
out.Captcha.Enabled = true
|
||||
out.Captcha.Provider = "hcaptcha"
|
||||
out.Captcha.Key = a.cfg.Security.Captcha.HCaptcha.Key
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "subscription-form", out)
|
||||
@@ -438,16 +450,39 @@ func (a *App) SubscriptionFormPage(c echo.Context) error {
|
||||
// SubscriptionForm handles subscription requests coming from public
|
||||
// HTML subscription forms.
|
||||
func (a *App) SubscriptionForm(c echo.Context) error {
|
||||
if !a.cfg.EnablePublicSubPage {
|
||||
return echo.NewHTTPError(http.StatusNotFound, a.i18n.T("public.invalidFeature"))
|
||||
|
||||
}
|
||||
|
||||
// If there's a nonce value, a bot could've filled the form.
|
||||
if c.FormValue("nonce") != "" {
|
||||
return echo.NewHTTPError(http.StatusBadGateway, a.i18n.T("public.invalidFeature"))
|
||||
}
|
||||
|
||||
// Process CAPTCHA.
|
||||
if a.cfg.Security.EnableCaptcha {
|
||||
err, ok := a.captcha.Verify(c.FormValue("h-captcha-response"))
|
||||
if a.captcha.IsEnabled() {
|
||||
var val string
|
||||
|
||||
// Get the appropriate captcha response field based on provider.
|
||||
switch a.captcha.GetProvider() {
|
||||
case captcha.ProviderHCaptcha:
|
||||
val = c.FormValue("h-captcha-response")
|
||||
case captcha.ProviderAltcha:
|
||||
val = c.FormValue("altcha")
|
||||
default:
|
||||
return c.Render(http.StatusBadRequest, tplMessage,
|
||||
makeMsgTpl(a.i18n.T("public.errorTitle"), "", a.i18n.T("public.invalidCaptcha")))
|
||||
}
|
||||
|
||||
if val == "" {
|
||||
return c.Render(http.StatusBadRequest, tplMessage,
|
||||
makeMsgTpl(a.i18n.T("public.errorTitle"), "", a.i18n.T("public.invalidCaptcha")))
|
||||
}
|
||||
|
||||
err, ok := a.captcha.Verify(val)
|
||||
if err != nil {
|
||||
a.log.Printf("Captcha request failed: %v", err)
|
||||
a.log.Printf("captcha request failed: %v", err)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
@@ -460,7 +495,7 @@ func (a *App) SubscriptionForm(c echo.Context) error {
|
||||
if err != nil {
|
||||
e, ok := err.(*echo.HTTPError)
|
||||
if !ok {
|
||||
return e
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Render(e.Code, tplMessage, makeMsgTpl(a.i18n.T("public.errorTitle"), "", fmt.Sprintf("%s", e.Message)))
|
||||
@@ -618,6 +653,25 @@ func (a *App) WipeSubscriberData(c echo.Context) error {
|
||||
makeMsgTpl(a.i18n.T("public.dataRemovedTitle"), "", a.i18n.T("public.dataRemoved")))
|
||||
}
|
||||
|
||||
// AltchaChallenge generates a challenge for Altcha captcha.
|
||||
func (a *App) AltchaChallenge(c echo.Context) error {
|
||||
// Check if Altcha is enabled.
|
||||
if !a.captcha.IsEnabled() || a.captcha.GetProvider() != captcha.ProviderAltcha {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "captcha not enabled")
|
||||
}
|
||||
|
||||
// Generate challenge.
|
||||
out, err := a.captcha.GenerateChallenge()
|
||||
if err != nil {
|
||||
a.log.Printf("error generating altcha challenge: %v", err)
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Error generating challenge")
|
||||
}
|
||||
|
||||
// Return the challenge as JSON.
|
||||
c.Response().Header().Set("Content-Type", "application/json")
|
||||
return c.String(http.StatusOK, out)
|
||||
}
|
||||
|
||||
// drawTransparentImage draws a transparent PNG of given dimensions
|
||||
// and returns the PNG bytes.
|
||||
func drawTransparentImage(h, w int) []byte {
|
||||
@@ -648,12 +702,6 @@ func (a *App) processSubForm(c echo.Context) (bool, error) {
|
||||
return false, echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("public.noListsSelected"))
|
||||
}
|
||||
|
||||
// If there's no name, use the name bit from the e-mail.
|
||||
req.Name = strings.TrimSpace(req.Name)
|
||||
if req.Name == "" {
|
||||
req.Name = strings.Split(req.Email, "@")[0]
|
||||
}
|
||||
|
||||
// Validate fields.
|
||||
if len(req.Email) > 1000 {
|
||||
return false, echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("subscribers.invalidEmail"))
|
||||
@@ -666,7 +714,10 @@ func (a *App) processSubForm(c echo.Context) (bool, error) {
|
||||
req.Email = em
|
||||
|
||||
req.Name = strings.TrimSpace(req.Name)
|
||||
if len(req.Name) == 0 || len(req.Name) > stdInputMaxLen {
|
||||
if len(req.Name) == 0 {
|
||||
// If there's no name, use the name bit from the e-mail.
|
||||
req.Name = strings.Split(req.Email, "@")[0]
|
||||
} else if len(req.Name) > stdInputMaxLen {
|
||||
return false, echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("subscribers.invalidName"))
|
||||
}
|
||||
|
||||
@@ -689,27 +740,33 @@ func (a *App) processSubForm(c echo.Context) (bool, error) {
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Status: models.SubscriberStatusEnabled,
|
||||
}, nil, listUUIDs, false)
|
||||
if err != nil {
|
||||
// Subscriber already exists. Update subscriptions in the DB.
|
||||
if e, ok := err.(*echo.HTTPError); ok && e.Code == http.StatusConflict {
|
||||
// Get the subscriber from the DB by their email.
|
||||
sub, err := a.core.GetSubscriber(0, "", req.Email)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Update the subscriber's subscriptions in the DB.
|
||||
_, hasOptin, err := a.core.UpdateSubscriberWithLists(sub.ID, sub, nil, listUUIDs, false, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return hasOptin, nil
|
||||
}
|
||||
|
||||
return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("%s", err.(*echo.HTTPError).Message))
|
||||
}, nil, listUUIDs, false, true)
|
||||
if err == nil {
|
||||
return hasOptin, nil
|
||||
}
|
||||
|
||||
return hasOptin, nil
|
||||
// Insert returned an error. Examine it.
|
||||
var lastErr = err
|
||||
|
||||
// Subscriber already exists. Update subscriptions in the DB.
|
||||
if e, ok := err.(*echo.HTTPError); ok && e.Code == http.StatusConflict {
|
||||
// Get the subscriber from the DB by their email.
|
||||
sub, err := a.core.GetSubscriber(0, "", req.Email)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Update the subscriber's subscriptions in the DB.
|
||||
_, hasOptin, err := a.core.UpdateSubscriberWithLists(sub.ID, sub, nil, listUUIDs, false, false, true)
|
||||
if err == nil {
|
||||
return hasOptin, nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
|
||||
// Something else went wrong.
|
||||
if e, ok := lastErr.(*echo.HTTPError); ok {
|
||||
return false, echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("%s", e.Message))
|
||||
}
|
||||
return false, echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T("public.errorProcessingRequest"))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/knadh/koanf/parsers/json"
|
||||
"github.com/knadh/koanf/providers/rawbytes"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/knadh/listmonk/internal/auth"
|
||||
"github.com/knadh/listmonk/internal/messenger/email"
|
||||
"github.com/knadh/listmonk/internal/notifs"
|
||||
"github.com/knadh/listmonk/models"
|
||||
@@ -73,7 +75,7 @@ func (a *App) GetSettings(c echo.Context) error {
|
||||
s.SendgridKey = strings.Repeat(pwdMask, utf8.RuneCountInString(s.SendgridKey))
|
||||
s.BouncePostmark.Password = strings.Repeat(pwdMask, utf8.RuneCountInString(s.BouncePostmark.Password))
|
||||
s.BounceForwardEmail.Key = strings.Repeat(pwdMask, utf8.RuneCountInString(s.BounceForwardEmail.Key))
|
||||
s.SecurityCaptchaSecret = strings.Repeat(pwdMask, utf8.RuneCountInString(s.SecurityCaptchaSecret))
|
||||
s.SecurityCaptcha.HCaptcha.Secret = strings.Repeat(pwdMask, utf8.RuneCountInString(s.SecurityCaptcha.HCaptcha.Secret))
|
||||
s.OIDC.ClientSecret = strings.Repeat(pwdMask, utf8.RuneCountInString(s.OIDC.ClientSecret))
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{s})
|
||||
@@ -219,13 +221,21 @@ func (a *App) UpdateSettings(c echo.Context) error {
|
||||
if set.BounceForwardEmail.Key == "" {
|
||||
set.BounceForwardEmail.Key = cur.BounceForwardEmail.Key
|
||||
}
|
||||
if set.SecurityCaptchaSecret == "" {
|
||||
set.SecurityCaptchaSecret = cur.SecurityCaptchaSecret
|
||||
if set.SecurityCaptcha.HCaptcha.Secret == "" {
|
||||
set.SecurityCaptcha.HCaptcha.Secret = cur.SecurityCaptcha.HCaptcha.Secret
|
||||
}
|
||||
if set.OIDC.ClientSecret == "" {
|
||||
set.OIDC.ClientSecret = cur.OIDC.ClientSecret
|
||||
}
|
||||
|
||||
// OIDC user auto-creation is enabled. Validate.
|
||||
if set.OIDC.AutoCreateUsers {
|
||||
if set.OIDC.DefaultUserRoleID.Int < auth.SuperAdminRoleID {
|
||||
return echo.NewHTTPError(http.StatusBadRequest,
|
||||
a.i18n.Ts("globals.messages.invalidFields", "name", a.i18n.T("settings.security.OIDCDefaultRole")))
|
||||
}
|
||||
}
|
||||
|
||||
for n, v := range set.UploadExtensions {
|
||||
set.UploadExtensions[n] = strings.ToLower(strings.TrimPrefix(strings.TrimSpace(v), "."))
|
||||
}
|
||||
@@ -247,6 +257,27 @@ func (a *App) UpdateSettings(c echo.Context) error {
|
||||
}
|
||||
set.DomainAllowlist = doms
|
||||
|
||||
// Validate and clean CORS domains.
|
||||
cors := make([]string, 0, len(set.SecurityCORSOrigins))
|
||||
for _, d := range set.SecurityCORSOrigins {
|
||||
if d = strings.TrimSpace(d); d != "" {
|
||||
if d == "*" {
|
||||
cors = append(cors, d)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse and validate the URL.
|
||||
u, err := url.Parse(d)
|
||||
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest,
|
||||
a.i18n.Ts("globals.messages.invalidData")+": invalid CORS domain: "+d)
|
||||
}
|
||||
// Save clean scheme + host
|
||||
cors = append(cors, u.Scheme+"://"+u.Host)
|
||||
}
|
||||
}
|
||||
set.SecurityCORSOrigins = cors
|
||||
|
||||
// Validate slow query caching cron.
|
||||
if set.CacheSlowQueries {
|
||||
if _, err := cron.ParseStandard(set.CacheSlowQueriesInterval); err != nil {
|
||||
|
||||
@@ -74,6 +74,25 @@ func (a *App) GetSubscriber(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, okResp{out})
|
||||
}
|
||||
|
||||
// GetSubscriberActivity handles the retrieval of a subscriber's campaign views and link clicks.
|
||||
func (a *App) GetSubscriberActivity(c echo.Context) error {
|
||||
user := auth.GetUser(c)
|
||||
|
||||
// Check if the user has access to at least one of the lists on the subscriber.
|
||||
id := getID(c)
|
||||
if err := a.hasSubPerm(user, []int{id}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch the subscriber activity from the DB.
|
||||
out, err := a.core.GetSubscriberActivity(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{out})
|
||||
}
|
||||
|
||||
// QuerySubscribers handles querying subscribers based on an arbitrary SQL expression.
|
||||
func (a *App) QuerySubscribers(c echo.Context) error {
|
||||
// Get the authenticated user.
|
||||
@@ -217,7 +236,7 @@ func (a *App) CreateSubscriber(c echo.Context) error {
|
||||
listIDs := user.FilterListsByPerm(auth.PermTypeManage, req.Lists)
|
||||
|
||||
// Insert the subscriber into the DB.
|
||||
sub, _, err := a.core.InsertSubscriber(req.Subscriber, listIDs, nil, req.PreconfirmSubs)
|
||||
sub, _, err := a.core.InsertSubscriber(req.Subscriber, listIDs, nil, req.PreconfirmSubs, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -256,7 +275,7 @@ func (a *App) UpdateSubscriber(c echo.Context) error {
|
||||
|
||||
// Update the subscriber in the DB.
|
||||
id := getID(c)
|
||||
out, _, err := a.core.UpdateSubscriberWithLists(id, req.Subscriber, listIDs, nil, req.PreconfirmSubs, true)
|
||||
out, _, err := a.core.UpdateSubscriberWithLists(id, req.Subscriber, listIDs, nil, req.PreconfirmSubs, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -458,7 +477,13 @@ func (a *App) BlocklistSubscribersByQuery(c echo.Context) error {
|
||||
|
||||
req.Search = strings.TrimSpace(req.Search)
|
||||
req.Query = formatSQLExp(req.Query)
|
||||
|
||||
if req.All {
|
||||
// If the "all" flag is set, ignore any subquery that may be present.
|
||||
req.Search = ""
|
||||
req.Query = ""
|
||||
} else if req.Search == "" && req.Query == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.Ts("globals.messages.invalidFields", "name", "query"))
|
||||
}
|
||||
// Does the user have the subscribers:sql_query permission?
|
||||
if req.Query != "" {
|
||||
if !user.HasPerm(auth.PermSubscribersSqlQuery) {
|
||||
|
||||
@@ -181,7 +181,7 @@ func (a *App) validateTxMessage(m models.TxMessage) (models.TxMessage, error) {
|
||||
}
|
||||
|
||||
for n, email := range m.SubscriberEmails {
|
||||
if m.SubscriberEmail != "" {
|
||||
if email != "" {
|
||||
em, err := a.importer.SanitizeEmail(email)
|
||||
if err != nil {
|
||||
return m, echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
|
||||
@@ -41,6 +41,8 @@ var migList = []migFunc{
|
||||
{"v4.0.0", migrations.V4_0_0},
|
||||
{"v4.1.0", migrations.V4_1_0},
|
||||
{"v5.0.0", migrations.V5_0_0},
|
||||
{"v5.1.0", migrations.V5_1_0},
|
||||
{"v5.2.0", migrations.V5_2_0},
|
||||
}
|
||||
|
||||
// upgrade upgrades the database to the current version by running SQL migration files
|
||||
|
||||
68
cmd/users.go
@@ -8,7 +8,9 @@ import (
|
||||
"github.com/knadh/listmonk/internal/auth"
|
||||
"github.com/knadh/listmonk/internal/core"
|
||||
"github.com/knadh/listmonk/internal/utils"
|
||||
"github.com/knadh/listmonk/models"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"gopkg.in/volatiletech/null.v6"
|
||||
)
|
||||
|
||||
@@ -267,6 +269,72 @@ func (a *App) UpdateUserProfile(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, okResp{out})
|
||||
}
|
||||
|
||||
// EnableTOTP enables TOTP 2FA for a user after verifying the code.
|
||||
func (a *App) EnableTOTP(c echo.Context) error {
|
||||
var (
|
||||
u = c.Get(auth.UserHTTPCtxKey).(auth.User)
|
||||
secret = strings.TrimSpace(c.FormValue("secret"))
|
||||
code = strings.TrimSpace(c.FormValue("code"))
|
||||
)
|
||||
|
||||
if secret == "" || code == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("globals.messages.invalidFields"))
|
||||
}
|
||||
|
||||
// If password login is disabled, can't enable TOTP.
|
||||
if !u.PasswordLogin {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("public.invalidFeature"))
|
||||
}
|
||||
|
||||
// If TOTP is already enabled, don't allow re-enabling.
|
||||
if u.TwofaType == models.TwofaTypeTOTP {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("users.twoFAAlreadyEnabled"))
|
||||
}
|
||||
|
||||
// Verify the TOTP code.
|
||||
valid := totp.Validate(code, secret)
|
||||
if !valid {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("users.invalidTOTPCode"))
|
||||
}
|
||||
|
||||
// Enable TOTP in the DB.
|
||||
if err := a.core.SetTwoFA(u.ID, models.TwofaTypeTOTP, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{true})
|
||||
}
|
||||
|
||||
// DisableTOTP disables TOTP 2FA for a user after verifying the password.
|
||||
func (a *App) DisableTOTP(c echo.Context) error {
|
||||
var (
|
||||
u = c.Get(auth.UserHTTPCtxKey).(auth.User)
|
||||
password = c.FormValue("password")
|
||||
)
|
||||
|
||||
// TOTP isn't enabled.
|
||||
if u.TwofaType != models.TwofaTypeTOTP {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.T("users.twoFANotEnabled"))
|
||||
}
|
||||
|
||||
// Validate password.
|
||||
if !strHasLen(password, 8, stdInputMaxLen) {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, a.i18n.Ts("globals.messages.invalidFields", "name", "password"))
|
||||
}
|
||||
|
||||
// Verify the password.
|
||||
if _, err := a.core.LoginUser(u.Username, password); err != nil {
|
||||
return echo.NewHTTPError(http.StatusForbidden, a.i18n.T("users.invalidPassword"))
|
||||
}
|
||||
|
||||
// Disable TOTP in the DB.
|
||||
if err := a.core.SetTwoFA(u.ID, models.TwofaTypeNone, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{true})
|
||||
}
|
||||
|
||||
// cacheUsers fetches (API) users and caches them in the auth module.
|
||||
// It also returns a bool indicating whether there are any actual users in the DB at all,
|
||||
// which if there aren't, the first time user setup needs to be run.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# All LISTMONK_* env variables also support the LISTMONK_*_FILE pattern for loading secrets from files with Docker secrets and Podman
|
||||
# eg: LISTMONK_ADMIN_USER -> LISTMONK_ADMIN_USER_FILE=/path/to/file_with_value
|
||||
|
||||
x-db-credentials: &db-credentials # Use the default POSTGRES_ credentials if they're available or simply default to "listmonk"
|
||||
POSTGRES_USER: &db-user listmonk # for database user, password, and database name
|
||||
POSTGRES_PASSWORD: &db-password listmonk
|
||||
|
||||
@@ -34,6 +34,31 @@ create_user() {
|
||||
create_group
|
||||
create_user
|
||||
|
||||
load_secret_files() {
|
||||
# Save and restore IFS
|
||||
old_ifs="$IFS"
|
||||
IFS='
|
||||
'
|
||||
# Capture all env variables starting with LISTMONK_ and ending with _FILE.
|
||||
# It's value is assumed to be a file path with its actual value.
|
||||
for line in $(env | grep '^LISTMONK_.*_FILE='); do
|
||||
var="${line%%=*}"
|
||||
fpath="${line#*=}"
|
||||
|
||||
# If it's a valid file, read its contents and assign it to the var
|
||||
# without the _FILE suffix.
|
||||
# Eg: LISTMONK_DB_USER_FILE=/run/secrets/user -> LISTMONK_DB_USER=$(contents of /run/secrets/user)
|
||||
if [ -f "$fpath" ]; then
|
||||
new_var="${var%_FILE}"
|
||||
export "$new_var"="$(cat "$fpath")"
|
||||
fi
|
||||
done
|
||||
IFS="$old_ifs"
|
||||
}
|
||||
|
||||
# Load env variables from files if LISTMONK_*_FILE variables are set.
|
||||
load_secret_files
|
||||
|
||||
# Try to set the ownership of the app directory to the app user.
|
||||
if ! chown -R ${PUID}:${PGID} /listmonk 2>/dev/null; then
|
||||
echo "Warning: Failed to change ownership of /listmonk. Readonly volume?"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# API / Campaigns
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|:-------|:----------------------------------------------------------------------------|:------------------------------------------|
|
||||
| :----- | :-------------------------------------------------------------------------- | :---------------------------------------- |
|
||||
| GET | [/api/campaigns](#get-apicampaigns) | Retrieve all campaigns. |
|
||||
| GET | [/api/campaigns/{campaign_id}](#get-apicampaignscampaign_id) | Retrieve a specific campaign. |
|
||||
| GET | [/api/campaigns/{campaign_id}/preview](#get-apicampaignscampaign_idpreview) | Retrieve preview of a campaign. |
|
||||
@@ -28,16 +28,16 @@ Retrieve all campaigns.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:---------|:---------|:---------|:---------------------------------------------------------------------|
|
||||
| order | string | | Sorting order: ASC for ascending, DESC for descending. |
|
||||
| order_by | string | | Result sorting field. Options: name, status, created_at, updated_at. |
|
||||
| query | string | | SQL query expression to filter campaigns. |
|
||||
| status | []string | | Status to filter campaigns. Repeat in the query for multiple values. |
|
||||
| tags | []string | | Tags to filter campaigns. Repeat in the query for multiple values. |
|
||||
| page | number | | Page number for paginated results. |
|
||||
| per_page | number | | Results per page. Set as 'all' for all results. |
|
||||
| no_body | boolean | | When set to true, returns response without body content. |
|
||||
| Name | Type | Required | Description |
|
||||
| :------- | :------- | :------- | :----------------------------------------------------------------------- |
|
||||
| order | string | | Sorting order: ASC for ascending, DESC for descending. |
|
||||
| order_by | string | | Result sorting field. Options: name, status, created_at, updated_at. |
|
||||
| query | string | | String to filtter by campaign name and subject (fulltext and substring). |
|
||||
| status | []string | | Status to filter campaigns. Repeat in the query for multiple values. |
|
||||
| tags | []string | | Tags to filter campaigns. Repeat in the query for multiple values. |
|
||||
| page | number | | Page number for paginated results. |
|
||||
| per_page | number | | Results per page. Set as 'all' for all results. |
|
||||
| no_body | boolean | | When set to true, returns response without body content. |
|
||||
|
||||
##### Example Response
|
||||
|
||||
@@ -93,10 +93,10 @@ Retrieve a specific campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:-------------|
|
||||
| campaign_id | number | Yes | Campaign ID. |
|
||||
| no_body | boolean | | When set to true, returns response without body content. |
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :------ | :------- | :------------------------------------------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID. |
|
||||
| no_body | boolean | | When set to true, returns response without body content. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
@@ -150,9 +150,9 @@ Preview a specific campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:------------------------|
|
||||
| campaign_id | number | Yes | Campaign ID to preview. |
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :---------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID to preview. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
@@ -175,9 +175,9 @@ Retrieve stats of specified campaigns.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:-------------------------------|
|
||||
| campaign_id | number | Yes | Campaign IDs to get stats for. |
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :----------------------------- |
|
||||
| campaign_id | number | Yes | Campaign IDs to get stats for. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
@@ -201,12 +201,12 @@ Retrieve stats of specified campaigns.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:----------------------------------------------|
|
||||
| id |number\[\] | Yes | Campaign IDs to get stats for. |
|
||||
| type |string | Yes | Analytics type: views, links, clicks, bounces |
|
||||
| from |string | Yes | Start value of date range. |
|
||||
| to |string | Yes | End value of date range. |
|
||||
| Name | Type | Required | Description |
|
||||
| :--- | :--------- | :------- | :-------------------------------------------- |
|
||||
| id | number\[\] | Yes | Campaign IDs to get stats for. |
|
||||
| type | string | Yes | Analytics type: views, links, clicks, bounces |
|
||||
| from | string | Yes | Start value of date range. |
|
||||
| to | string | Yes | End value of date range. |
|
||||
|
||||
|
||||
##### Example Request
|
||||
@@ -289,7 +289,7 @@ Create a new campaign.
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:-------------|:-----------|:---------|:----------------------------------------------------------------------------------------|
|
||||
| :----------- | :--------- | :------- | :-------------------------------------------------------------------------------------- |
|
||||
| name | string | Yes | Campaign name. |
|
||||
| subject | string | Yes | Campaign email subject. |
|
||||
| lists | number\[\] | Yes | List IDs to send campaign to. |
|
||||
@@ -357,8 +357,8 @@ Use the same parameters in [POST /api/campaigns](#post-apicampaigns) in addition
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:---------|:---------|:---------------------------------------------------|
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :--------- | :------- | :------------------------------------------------- |
|
||||
| subscribers | string\[\] | Yes | List of subscriber e-mails to send the message to. |
|
||||
|
||||
______________________________________________________________________
|
||||
@@ -385,10 +385,10 @@ Change status of a campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:------------------------------------------------------------------------|
|
||||
| campaign_id | number | Yes | Campaign ID to change status. |
|
||||
| status | string | Yes | New status for campaign: 'scheduled', 'running', 'paused', 'cancelled'. |
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :---------------------------------------------------------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID to change status. |
|
||||
| status | string | Yes | New status for campaign: 'scheduled', 'running', 'paused', 'cancelled'. |
|
||||
|
||||
##### Note
|
||||
|
||||
@@ -450,13 +450,13 @@ Publish campaign to public archive.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:-------------------|:-----------|:---------|:-------------------------------------------------------------------------|
|
||||
| campaign_id | number | Yes | Campaign ID to publish to public archive. |
|
||||
| archive | bool | Yes | State of the public archive. |
|
||||
| archive_template_id| number | No | Archive template id. Defaults to 0. |
|
||||
| archive_meta | JSON string| No | Optional Metadata to use in campaign message or template.Eg: name, email.|
|
||||
| archive_slug | string | No | Name for page to be used in public archive URL |
|
||||
| Name | Type | Required | Description |
|
||||
| :------------------ | :---------- | :------- | :------------------------------------------------------------------------ |
|
||||
| campaign_id | number | Yes | Campaign ID to publish to public archive. |
|
||||
| archive | bool | Yes | State of the public archive. |
|
||||
| archive_template_id | number | No | Archive template id. Defaults to 0. |
|
||||
| archive_meta | JSON string | No | Optional Metadata to use in campaign message or template.Eg: name, email. |
|
||||
| archive_slug | string | No | Name for page to be used in public archive URL |
|
||||
|
||||
|
||||
##### Example Request
|
||||
@@ -489,9 +489,9 @@ Delete a campaign.
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|:------------|:----------|:---------|:-----------------------|
|
||||
| campaign_id | number | Yes | Campaign ID to delete. |
|
||||
| Name | Type | Required | Description |
|
||||
| :---------- | :----- | :------- | :--------------------- |
|
||||
| campaign_id | number | Yes | Campaign ID to delete. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
|
||||
@@ -71,8 +71,8 @@ Send a CSV (optionally ZIP compressed) file to import subscribers. Use a multipa
|
||||
|:----------|:---------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| mode | string | Yes | `subscribe` or `blocklist` |
|
||||
| delim | string | Yes | Single character indicating delimiter used in the CSV file, eg: `,` |
|
||||
| lists | []number | Yes | Single character indicating delimiter used in the CSV file, eg: `,` |
|
||||
| overwrite | bool | Yes | Whether to overwrite the subscriber parameters including subscriptions or ignore records that are already present in the database. |
|
||||
| lists | []number | | Array of list IDs to subscribe to. |
|
||||
| overwrite | bool | | Whether to overwrite the subscriber parameters including subscriptions or ignore records that are already present in the database. |
|
||||
|
||||
##### Example Request
|
||||
|
||||
|
||||
@@ -20,3 +20,5 @@ A list of 3rd party client libraries and SDKs that have been written for listmon
|
||||
- [listmonk-laravel](https://github.com/theafolayan/listmonk-laravel) — Laravel API Client
|
||||
- [nuxt-listmonk](https://github.com/roncallyt/nuxt-listmonk) — Listmonk module for Nuxt.js
|
||||
- [listmonk-japi](https://codeberg.org/hlassiege/listmonk-japi) - Listmonk client for Java/kotlin
|
||||
- [listmonk-mcp](https://github.com/rhnvrm/listmonk-mcp) — MCP (Model Context Protocol) server for Claude integration
|
||||
- [N8N Nodes](https://github.com/wiesinghilker/n8n-nodes-listmonk) — Adds Listmonk Nodes for N8N Automations
|
||||
|
||||
@@ -20,6 +20,7 @@ Allows sending transactional messages to one or more subscribers via a preconfig
|
||||
| subscriber_ids | number\[\] | | Multiple subscriber IDs as an alternative to `subscriber_id`. |
|
||||
| template_id | number | Yes | ID of the transactional template to be used for the message. |
|
||||
| from_email | string | | Optional sender email. |
|
||||
| subject | string | | Optional subject. If empty, the subject defined on the template is used |
|
||||
| data | JSON | | Optional nested JSON map. Available in the template as `{{ .Tx.Data.* }}`. |
|
||||
| headers | JSON\[\] | | Optional array of email headers. |
|
||||
| messenger | string | | Messenger to send the message. Default is `email`. |
|
||||
|
||||
@@ -14,6 +14,9 @@ Configure the bounce mailbox in Settings -> Bounces. Either the "From" e-mail th
|
||||
|
||||
Some mail servers may also return the bounce to the `Reply-To` address, which can also be added to the header settings.
|
||||
|
||||
### Bounce classification
|
||||
listmonk applies a series of heuristics looking for keywords in the bounced mail body to guess if it is a 'soft' bounce or a 'hard' bounce. For instance, 4.x.x and 5.x.x error status codes, common strings such as "mailbox not found" etc. If none of the heuristics match, then the bounce mail is considered to be 'soft' by default.
|
||||
|
||||
## Webhook API
|
||||
The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.
|
||||
|
||||
@@ -22,14 +25,14 @@ The bounce webhook API can be used to record bounce events with custom scripting
|
||||
| `POST` | /webhooks/bounce | Record a bounce event. |
|
||||
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| ----------------| --------- | -----------| ------------------------------------------------------------------------------------ |
|
||||
| subscriber_uuid | string | | The UUID of the subscriber. Either this or `email` is required. |
|
||||
| email | string | | The e-mail of the subscriber. Either this or `subscriber_uuid` is required. |
|
||||
| campaign_uuid | string | | UUID of the campaign for which the bounce happened. |
|
||||
| source | string | Yes | A string indicating the source, eg: `api`, `my_script` etc. |
|
||||
| type | string | Yes | `hard` or `soft` bounce. Currently, this has no effect on how the bounce is treated. |
|
||||
| meta | string | | An optional escaped JSON string with arbitrary metadata about the bounce event. |
|
||||
| Name | Type | Required | Description |
|
||||
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------ |
|
||||
| subscriber_uuid | string | | The UUID of the subscriber. Either this or `email` is required. |
|
||||
| email | string | | The e-mail of the subscriber. Either this or `subscriber_uuid` is required. |
|
||||
| campaign_uuid | string | | UUID of the campaign for which the bounce happened. |
|
||||
| source | string | Yes | A string indicating the source, eg: `api`, `my_script` etc. |
|
||||
| type | string | Yes | `hard` or `soft` bounce. Currently, this has no effect on how the bounce is treated. |
|
||||
| meta | string | | An optional escaped JSON string with arbitrary metadata about the bounce event. |
|
||||
|
||||
|
||||
```shell
|
||||
@@ -43,11 +46,11 @@ curl -u 'api_username:access_token' -X POST 'http://localhost:9000/webhooks/boun
|
||||
listmonk supports receiving bounce webhook events from the following SMTP providers.
|
||||
|
||||
| Endpoint | Description | More info |
|
||||
|:--------------------------------------------------------------|:---------------------------------------|:----------------------------------------------------------------------------------------------------------------------|
|
||||
| :------------------------------------------------------------ | :------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
|
||||
| `https://listmonk.yoursite.com/webhooks/service/ses` | Amazon (AWS) SES | See below |
|
||||
| `https://listmonk.yoursite.com/webhooks/service/sendgrid` | Sendgrid / Twilio Signed event webhook | [More info](https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features) |
|
||||
| `https://listmonk.yoursite.com/webhooks/service/postmark` | Postmark webhook | [More info](https://postmarkapp.com/developer/webhooks/webhooks-overview) |
|
||||
| `https://listmonk.yoursite.com/webhooks/service/forwardemail` | Forward Email webhook | [More info](https://forwardemail.net/en/faq#do-you-support-bounce-webhooks) |
|
||||
| `https://listmonk.yoursite.com/webhooks/service/forwardemail` | Forward Email webhook | [More info](https://forwardemail.net/en/faq#do-you-support-bounce-webhooks) |
|
||||
|
||||
## Amazon Simple Email Service (SES)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
### TOML Configuration file
|
||||
One or more TOML files can be read by passing `--config config.toml` multiple times. Apart from a few low level configuration variables and the database configuration, all other settings can be managed from the `Settings` dashboard on the admin UI.
|
||||
|
||||
To generate a new sample configuration file, run `--listmonk --new-config`
|
||||
To generate a new sample configuration file, run `listmonk --new-config`
|
||||
|
||||
### Environment variables
|
||||
Variables in config.toml can also be provided as environment variables prefixed by `LISTMONK_` with periods replaced by `__` (double underscore). To start listmonk purely with environment variables without a configuration file, set the environment variables and pass the config flag as `--config=""`.
|
||||
|
||||
@@ -111,6 +111,8 @@ $ helm upgrade \
|
||||
<br />
|
||||
<a href="https://www.pikapods.com/pods?run=listmonk"><img src="https://www.pikapods.com/static/run-button.svg" alt="Deploy on PikaPod" style="max-width: 150px;" /></a>
|
||||
<br />
|
||||
<a href="https://northflank.com/stacks/deploy-listmonk"><img src="https://assets.northflank.com/deploy_to_northflank_smm_36700fb050.svg" alt="One-click deploy on Northflank" height="35" style="max-width: 150px; border-radius: 6px; object-fit: contain;" /></a>
|
||||
<br />
|
||||
<a href="https://railway.app/new/template/listmonk"><img src="https://railway.app/button.svg" alt="One-click deploy on Railway" style="max-width: 150px;" /></a>
|
||||
<br />
|
||||
<a href="https://repocloud.io/details/?app_id=217"><img src="https://d16t0pc4846x52.cloudfront.net/deploy.png" alt="Deploy at RepoCloud" style="max-width: 150px;"/></a>
|
||||
@@ -132,4 +134,4 @@ $ helm upgrade \
|
||||
* [*Docker on Rocky Linux 8* (nginx, Let's Encrypt SSL)](https://wiki.crowncloud.net/?How_to_Install_Listmonk_with_Docker_on_Rocky_Linux_8)
|
||||
* [*Docker* with nginx reverse proxy, certbot SSL, and Gmail SMTP](https://www.maketecheasier.com/create-own-newsletter-with-listmonk/)
|
||||
* [Install Listmonk on Self-hosting with *Pre-Configured AMI Package at AWS* by Single Click](https://meetrix.io/articles/how-to-install-llama-2-on-aws-with-pre-configured-ami-package/)
|
||||
* [Tutorial for deploying on *Fly.io*](https://github.com/paulrudy/listmonk-on-fly) -- Currently [not working](https://github.com/knadh/listmonk/issues/984#issuecomment-1694545255)
|
||||
* [*Fly.io* working example](https://gitlab.com/votelog/apps/newsletter)
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
|
||||
Listmonk supports single sign-on with OIDC (OpenID Connect). Any standards compliant OIDC provider can be configured in Settings -> Security -> OIDC
|
||||
|
||||
!!! note "Automatic user creation"
|
||||
There is no support for automatic user creation via OIDC currently. The Super Admin must create users prior in Admin -> Users with the same e-mail address that is expected from the OIDC provider per user.
|
||||
|
||||
### User auto-creation
|
||||
If `Settings -> Security -> OIDC -> Auto-create users` is turned on, when users login via OIDC, an account is auto-created if an existing account is not found (based on the OIDC e-mail ID).
|
||||
|
||||
# Tutorials
|
||||
|
||||
@@ -37,10 +36,12 @@ After the client creation steps above, go to the client's `Credentials` tab and
|
||||
### 2. Configure Listmonk
|
||||
2. In Listmonk Admin -> Settings -> Security -> OIDC.
|
||||
- **Enable OIDC SSO**: Turn on
|
||||
- **Provider URL**: `https://keycloak.yoursite.com/auth/realms/{realm}` (replace `{realm}` with the chosen realm name)
|
||||
- **Provider URL**: `https://keycloak.yoursite.com/realms/{realm}` (replace `{realm}` with the chosen realm name). This URL is as of v26.3 and may differ across Keycloak versions.
|
||||
- **Provider name**: Set a name to show on the listmonk login form, eg: `Login with OrgName`
|
||||
- **Client ID**: Client ID set in Keycloak, eg: `listmonk`
|
||||
- **Client Secret**: Client Secret copied from Keycloak
|
||||
- **Auto-create users from SSO**: (Optional) Enable to automatically create users who don't exist
|
||||
- **Default user role**: (Required if auto-create enabled) Select role for new users
|
||||
|
||||
|
||||
|
||||
@@ -75,3 +76,5 @@ In listmonk Admin → Settings → Security → OIDC:
|
||||
- **Provider Name**: Set a name to show on the login form (e.g., `Login with OrgName`)
|
||||
- **Client ID**: Client ID set in Authentik (e.g., `listmonk`)
|
||||
- **Client Secret**: Client Secret copied from Authentik
|
||||
- **Auto-create users from SSO**: (Optional) Enable to automatically create users who don't exist
|
||||
- **Default user role**: (Required if auto-create enabled) Select role for new users
|
||||
|
||||
@@ -12,7 +12,7 @@ A user role is a collection of user related permissions. User roles are attached
|
||||
| | subscribers:get_all | Get all subscribers and their details |
|
||||
| | subscribers:manage | Add, update, and delete subscribers |
|
||||
| | subscribers:import | Import subscribers from external files |
|
||||
| | subscribers:sql_query | Run SQL queries on subscriber data. **WARNING:** This permission will allow the querying of all lists and subscribers directly from the database with SQL expressions, superceding individual list and subscriber permissions above. |
|
||||
| | subscribers:sql_query | Run raw SQL queries on subscriber data. **WARNING:** This permission allows execution of arbitrary SQL expressions and SQL functions. While it is a readonly feature designed to allow querying of all lists and subscribers directly from the database superceding individual list and subscriber permissions above, raw SQL expressions makes it possible to obtain Postgres database configuration such as version and paths. Give this permission only to trusted users. |
|
||||
| | tx:send | Send transactional messages to subscribers |
|
||||
| campaigns | campaigns:get | Get and view campaigns belonging to permitted lists |
|
||||
| | campaigns:get_all | Get and view campaigns across all lists |
|
||||
@@ -26,7 +26,7 @@ A user role is a collection of user related permissions. User roles are attached
|
||||
| templates | templates:get | Get email templates |
|
||||
| | templates:manage | Create, update, and delete templates |
|
||||
| users | users:get | Get system user accounts |
|
||||
| | users:manage | Create, update, and delete user accounts |
|
||||
| | users:manage | Create, update, and delete user accounts **WARNING:** This permission allows creation of users with any role, including Super Admin. This permission should only be given to Super Admin level accounts |
|
||||
| | roles:get | Get user roles and permissions |
|
||||
| | roles:manage | Create and modify user roles |
|
||||
| settings | settings:get | Get system settings |
|
||||
|
||||
@@ -4,6 +4,9 @@ A template is a re-usable HTML design that can be used across campaigns and tran
|
||||
|
||||
listmonk supports [Go template](https://pkg.go.dev/text/template) expressions that lets you create powerful, dynamic HTML templates. It also integrates 100+ useful [Sprig template functions](https://masterminds.github.io/sprig/).
|
||||
|
||||
!!! Warning
|
||||
Sprig template functions are powerful and Turing-complete, allowing programming of complex behaviour in templates. This means that it is also possible to program undesired behaviour, such as overloading memory on the host by concatenating large strings in a loop. Ensure that templating (campaigns, templates) permissions are given only to trusted users.
|
||||
|
||||
## Campaign templates
|
||||
Campaign templates are used in an e-mail campaigns. These template are created and managed on the UI under `Campaigns -> Templates`, and are selected when creating new campaigns.
|
||||
|
||||
@@ -28,6 +31,8 @@ There are several template functions and expressions that can be used in campaig
|
||||
| `{{ .Subscriber.CreatedAt }}` | Timestamp when the subscriber was first added |
|
||||
| `{{ .Subscriber.UpdatedAt }}` | Timestamp when the subscriber was modified |
|
||||
|
||||
### Campaigns
|
||||
|
||||
| Expression | Description |
|
||||
| --------------------- | -------------------------------------------------------- |
|
||||
| `{{ .Campaign.UUID }}` | The randomly generated unique ID of the campaign |
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"v5.0.0","date":"2025-04-28T19:00:04Z","url":"https://github.com/knadh/listmonk/releases/tag/v5.0.0","assets":[{"name":"darwin","url":"https://github.com/knadh/listmonk/releases/download/v5.0.0/listmonk_5.0.0_darwin_amd64.tar.gz"},{"name":"freebsd","url":"https://github.com/knadh/listmonk/releases/download/v5.0.0/listmonk_5.0.0_freebsd_amd64.tar.gz"},{"name":"linux","url":"https://github.com/knadh/listmonk/releases/download/v5.0.0/listmonk_5.0.0_linux_amd64.tar.gz"},{"name":"netbsd","url":"https://github.com/knadh/listmonk/releases/download/v5.0.0/listmonk_5.0.0_netbsd_amd64.tar.gz"},{"name":"openbsd","url":"https://github.com/knadh/listmonk/releases/download/v5.0.0/listmonk_5.0.0_openbsd_amd64.tar.gz"},{"name":"windows","url":"https://github.com/knadh/listmonk/releases/download/v5.0.0/listmonk_5.0.0_windows_amd64.tar.gz"}]}
|
||||
{"version":"v5.1.0","date":"2025-09-09T17:58:16Z","url":"https://github.com/knadh/listmonk/releases/tag/v5.1.0","assets":[{"name":"darwin","url":"https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_darwin_amd64.tar.gz"},{"name":"freebsd","url":"https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_freebsd_amd64.tar.gz"},{"name":"linux","url":"https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_linux_amd64.tar.gz"},{"name":"netbsd","url":"https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_netbsd_amd64.tar.gz"},{"name":"openbsd","url":"https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_openbsd_amd64.tar.gz"},{"name":"windows","url":"https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_windows_amd64.tar.gz"}]}
|
||||
|
||||
@@ -34,12 +34,20 @@
|
||||
<div class="col-6">
|
||||
<div class="box">
|
||||
<h3>Binary</h3>
|
||||
<p>Download binary (64 bit)</p>
|
||||
<div class="download-links">
|
||||
{{ range.Page.Site.Data.github.assets }}
|
||||
<a href="{{ .url }}" class="item">
|
||||
<span class="icon">
|
||||
<img src="{{ .Site.BaseURL }}static/images/logo-{{ .name }}.svg" alt="{{ .name | title }}" title="{{ .name | title }}" />
|
||||
</span>
|
||||
<span class="name">{{ .name | title }}</span>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<p>Install</p>
|
||||
<ul class="install-steps">
|
||||
<li class="download-links">Download binary:<br />
|
||||
{{ range.Page.Site.Data.github.assets }}
|
||||
<a href="{{ .url }}">{{ .name | title }}</a>
|
||||
{{ end }}
|
||||
</li>
|
||||
<li>
|
||||
<code>./listmonk --new-config</code> to generate config.toml. Edit it.
|
||||
</li>
|
||||
@@ -47,17 +55,6 @@
|
||||
<li>Run <code>./listmonk</code> and visit <code>http://localhost:9000</code></li>
|
||||
</ul>
|
||||
<p><a href="/docs/installation">Installation docs →</a></p>
|
||||
|
||||
<br />
|
||||
<h3>Hosting providers</h3>
|
||||
<a href="https://railway.app/new/template/listmonk"><img src="https://railway.app/button.svg" alt="One-click deploy on Railway" style="max-height: 32px;" /></a>
|
||||
<br />
|
||||
<a href="https://www.pikapods.com/pods?run=listmonk"><img src="https://www.pikapods.com/static/run-button.svg" alt="Deploy on PikaPod" /></a>
|
||||
<br />
|
||||
<a href="https://dash.elest.io/deploy?soft=Listmonk&id=237"><img height="33" src="https://raw.githubusercontent.com/elestio-examples/reactjs/refs/heads/master/src/deploy-on-elestio.png" alt="Deploy on Elestio" /></a>
|
||||
<br />
|
||||
<a href="https://zeabur.com/templates/5EDMN6"><img width="148" src="https://zeabur.com/button.svg" alt="Deploy on Zeabur"/></a>
|
||||
<p><small>*listmonk is not affiliated to these providers</small></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@@ -84,119 +81,123 @@ docker compose up -d
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="hosting">
|
||||
<div class="container">
|
||||
<h2 class="center">Hosting providers</h2><br />
|
||||
<div>
|
||||
<a href="https://www.nodion.com/en/deploy/listmonk/"><img src="https://nodion-static.nodioncdn.com/nodion-button-s.svg" alt="Deploy to Nodion" style="max-height: 32px;" /></a>
|
||||
<a href="https://www.kloudbean.com/listmonk-self-hosted"><img src="https://storage-basic.kloudbean.com/opensource/deploy_on_kloudbean_listmonk.svg" alt="One-click deploy on Kloudbean" style="max-height: 32px;" /></a>
|
||||
<a href="https://northflank.com/stacks/deploy-listmonk"><img src="https://assets.northflank.com/deploy_to_northflank_smm_36700fb050.svg" alt="One-click deploy on Northflank" style="height: 32px; width: 150px; border-radius: 6px; object-fit: contain;" /></a>
|
||||
<a href="https://railway.app/new/template/listmonk"><img src="https://railway.app/button.svg" alt="One-click deploy on Railway" style="max-height: 32px;" /></a>
|
||||
<a href="https://www.pikapods.com/pods?run=listmonk"><img src="https://www.pikapods.com/static/run-button.svg" alt="Deploy on PikaPod" /></a>
|
||||
<a href="https://elest.io/open-source/listmonk"><img height="33" src="https://raw.githubusercontent.com/elestio-examples/reactjs/refs/heads/master/src/deploy-on-elestio.png" alt="Deploy on Elestio" /></a>
|
||||
<a href="https://zeabur.com/templates/5EDMN6"><img width="148" src="https://zeabur.com/button.svg" alt="Deploy on Zeabur"/></a>
|
||||
</div>
|
||||
<p class="disclaimer">*listmonk has no affiliation with these providers</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="container">
|
||||
<section class="lists feature">
|
||||
<h2>One-way mailing lists</h2>
|
||||
<div class="center">
|
||||
<section class="row feature">
|
||||
<div class="col-5">
|
||||
<h2>One-way mailing lists</h2>
|
||||
<p>
|
||||
Manage millions of subscribers across single and double opt-in lists. Query and segment subscribers with SQL expressions.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-1"></div>
|
||||
<div class="col-6 right">
|
||||
<img class="box" src="static/images/lists.png" alt="Screenshot of list management feature" />
|
||||
</div>
|
||||
<p>
|
||||
Manage millions of subscribers across many single and double opt-in one-way mailing lists
|
||||
with custom JSON attributes for each subscriber.
|
||||
Query and segment subscribers with SQL expressions.
|
||||
</p>
|
||||
<p>Use the fast bulk importer (~10k records per second) or use HTTP/JSON APIs or interact with the simple
|
||||
table schema to integrate external CRMs and subscriber databases.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="tx feature">
|
||||
<h2>For everyone</h2>
|
||||
<p>
|
||||
From easy user management with fine-grained permissions,
|
||||
to internationalization in 34+ languages, listmonk is for everyone—individuals, small teams, and large organizations everywhere.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="tx feature">
|
||||
<h2>Transactional mails</h2>
|
||||
<div class="center">
|
||||
<img class="box" src="static/images/tx.png" alt="Screenshot of transactional API" />
|
||||
<section class="row media feature">
|
||||
<div class="col-5">
|
||||
<h2>Analytics</h2>
|
||||
<p>
|
||||
Built-in analytics to visualize campaign performance, bounces, top links and more across campaigns.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Simple API to send arbitrary transactional messages to subscribers
|
||||
using pre-defined templates. Send messages as e-mail, SMS, Whatsapp messages or any medium via Messenger interfaces.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="media feature">
|
||||
<h2>Analytics</h2>
|
||||
<div class="center">
|
||||
<div class="col-1"></div>
|
||||
<div class="col-6 right">
|
||||
<img class="box" src="static/images/analytics.png" alt="Screenshot of analytics feature" />
|
||||
</div>
|
||||
<p class="center">
|
||||
Simple analytics and visualizations. Connect external visualization programs to the database easily with the simple table structure.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="templating feature">
|
||||
<h2>Templating</h2>
|
||||
<div class="center">
|
||||
<img class="box" src="static/images/templating.png" alt="Screenshot of templating feature" />
|
||||
</div>
|
||||
<section class="row templating feature">
|
||||
<div class="col-5">
|
||||
<h2>Templating</h2>
|
||||
<p>
|
||||
Create powerful, dynamic e-mail templates with the <a href="https://golang.org/pkg/text/template/">Go templating language</a>.
|
||||
Use template expressions, logic, and 100+ functions in subject lines and content.
|
||||
Write HTML e-mails using a visual drag-and-drop builder, a WYSIWYG editor, Markdown, raw syntax-highlighted HTML, or just plain text.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-1"></div>
|
||||
<div class="col-6 right">
|
||||
<img class="box" src="static/images/templating.png" alt="Screenshot of templating feature" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="performance feature">
|
||||
<h2>Performance</h2>
|
||||
<div class="center">
|
||||
<section class="row performance feature">
|
||||
<div class="col-5">
|
||||
<h2>Performance</h2>
|
||||
<p>
|
||||
Multi-threaded, high-throughput, multi-SMTP e-mail queues.
|
||||
Throughput and sliding window rate limiting for fine grained control.
|
||||
Single binary application with nominal CPU and memory footprint that runs everywhere.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-1"></div>
|
||||
<div class="col-6 right">
|
||||
<figure class="box">
|
||||
<img src="static/images/performance.png" alt="Screenshot of performance metrics" />
|
||||
|
||||
<figcaption>
|
||||
<figcaption style="font-size:">
|
||||
A production listmonk instance sending a campaign of 7+ million e-mails.<br />
|
||||
CPU usage is a fraction of a single core with peak RAM usage of 57 MB.
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<br />
|
||||
<p>
|
||||
Multi-threaded, high-throughput, multi-SMTP e-mail queues.
|
||||
Throughput and sliding window rate limiting for fine grained control.
|
||||
Single binary application with nominal CPU and memory footprint that runs everywhere.
|
||||
The only dependency is a Postgres (⩾ 12) database.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="media feature">
|
||||
<h2>Media</h2>
|
||||
<div class="center">
|
||||
<img class="box" src="static/images/media.png" alt="Screenshot of media feature" />
|
||||
<section class="row tx feature">
|
||||
<div class="col-5">
|
||||
<h2>Transactional mails</h2>
|
||||
<p>
|
||||
Simple API to send arbitrary transactional messages to subscribers
|
||||
using pre-defined templates. Send messages as e-mail, SMS, Whatsapp messages or any medium via Messenger interfaces.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-1"></div>
|
||||
<div class="col-6 right">
|
||||
<img class="box" src="static/images/tx.png" alt="Screenshot of transactional API" />
|
||||
</div>
|
||||
<p class="center">Use the media manager to upload images for e-mail campaigns
|
||||
on the server's filesystem, Amazon S3, or any S3 compatible (Minio) backend.</p>
|
||||
</section>
|
||||
|
||||
<section class="lists feature">
|
||||
<h2>Extensible</h2>
|
||||
<div class="center">
|
||||
<section class="row lists feature">
|
||||
<div class="col-5">
|
||||
<h2>Extensible</h2>
|
||||
<p>
|
||||
More than just e-mail campaigns. Messenger HTTP webhooks to send SMS,
|
||||
Whatsapp, FCM notifications, or any type of messages.
|
||||
Extensive API coverage for all features.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-1"></div>
|
||||
<div class="col-6 right">
|
||||
<img class="box" src="static/images/messengers.png" alt="Screenshot of Messenger feature" />
|
||||
</div>
|
||||
<p class="center">
|
||||
More than just e-mail campaigns. Connect HTTP webhooks to send SMS,
|
||||
Whatsapp, FCM notifications, or any type of messages.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="privacy feature">
|
||||
<h2>Privacy</h2>
|
||||
<div class="center">
|
||||
<img class="box" src="static/images/privacy.png" alt="Screenshot of privacy features" />
|
||||
</div>
|
||||
<p class="center">
|
||||
Allow subscribers to permanently blocklist themselves, export all their data,
|
||||
and to wipe all their data in a single click.
|
||||
<section class="row feature center">
|
||||
<h2>And a lot more ...</h2>
|
||||
<p>
|
||||
Full privacy control for subscribers, OIDC SSO authentication with granular roles and permissions, granular API tokens, media library with S3-compatible backend and a lot more.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h2 class="center">and a lot more …</h2>
|
||||
|
||||
<div class="center">
|
||||
<br />
|
||||
<a href="#download" class="button">Download</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
<footer class="container footer">
|
||||
© 2019-{{ now.Format "2006" }} / <a href="https://nadh.in">Kailash Nadh</a>
|
||||
© 2018-{{ now.Format "2006" }} / <a href="https://nadh.in">Kailash Nadh</a>
|
||||
</footer>
|
||||
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
|
||||
|
Before Width: | Height: | Size: 360 KiB |
|
Before Width: | Height: | Size: 372 KiB |
62
docs/site/static/static/images/logo-darwin.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 4.9345538 4.9345534"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="logo-mac.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview8"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="5.6568542"
|
||||
inkscape:cx="38.095378"
|
||||
inkscape:cy="13.169864"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1367"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g8" />
|
||||
<g
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
id="g8"
|
||||
transform="translate(-53.903803,-46.879577)">
|
||||
<g
|
||||
id="g9"
|
||||
transform="matrix(0.04313043,0,0,0.04313043,54.02643,46.860281)">
|
||||
<path
|
||||
d="m 58.787198,84.737433 c -13.534,0.527 -28.121,-2.021 -40.423,-8.173 l 2.548,-4.042 c 11.951,5.624 25.66,7.558 37.875,7.03 0,0.439 0.525,-8.261 1.316,-13.006 h -18.365 c 1.669,-19.157 7.557,-36.205 16.345,-50.704 h -54.922 v 83.658 h 57.733 c -1.318,-5.01 -2.107,-14.939 -2.107,-14.763 z"
|
||||
fill="#d0e9fb"
|
||||
id="path2" />
|
||||
<path
|
||||
d="m 64.322198,79.200433 c 10.018,-0.967 18.805,-3.603 24.166,-7.03 l 3.164,4.042 c -7.383,4.307 -16.961,7.118 -27.33,8.173 0.264,5.185 1.055,10.105 2.373,15.115 H 106.2382 v -83.658 H 64.146198 c -8.963,13.709 -14.06,28.999 -15.993,45.168 h 18.894 c -1.583,6.151 -2.549,12.215 -2.725,18.19 z m -36.205,-50.704 h 5.361 v 12.391 h -5.361 z"
|
||||
fill="#00acec"
|
||||
id="path3" />
|
||||
<path
|
||||
d="m 72.757198,28.496433 h 5.362 v 12.391 h -5.362 z"
|
||||
id="path4" />
|
||||
<path
|
||||
d="m 64.322198,79.200433 c 10.018,-0.967 18.805,-3.603 24.166,-7.03 l 3.164,4.042 c -7.383,4.307 -16.961,7.118 -27.33,8.173 0.439,9.314 2.725,18.540997 7.293,27.416997 l -4.568,2.459 c -8.348,-13.18 -9.842,-30.930997 -6.943,-47.714997 h -18.366 c 2.285,-26.099 12.302,-48.244 26.89,-65.4669996 l 3.953,3.427 c -14.234,16.5209996 -22.055,35.7659996 -24.428,56.5039996 h 18.894 c -1.583,6.151 -2.549,12.215 -2.725,18.19 z"
|
||||
id="path5" />
|
||||
<path
|
||||
d="m 58.787198,79.552433 v 5.185 c -13.534,0.527 -28.121,-2.021 -40.423,-8.173 l 2.548,-4.042 c 11.95,5.624 25.659,7.557 37.875,7.03 z"
|
||||
fill="#00acec"
|
||||
id="path6" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
46
docs/site/static/static/images/logo-freebsd.svg
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="64.000008"
|
||||
height="64.000015"
|
||||
viewBox="0 0 6.5536008 6.5534351"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
sodipodi:docname="logo-freebsd.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="138"
|
||||
inkscape:cy="38"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1367"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<g
|
||||
fill="#b5010f"
|
||||
id="g2"
|
||||
transform="matrix(0.02571861,0,0,0.02612165,-0.02108463,-0.02480991)">
|
||||
<path
|
||||
d="m 252.723,5.11 c 13.508,13.5 -23.939,72.848 -30.27,79.182 -6.33,6.321 -22.409,0.505 -35.91,-13 -13.508,-13.5 -19.327,-29.583 -12.996,-35.914 6.327,-6.333 65.671,-43.777 79.176,-30.269 M 63.305,19.394 C 42.683,7.694 13.339,-5.322 4.005,4.014 -5.453,13.468 8.039,43.472 19.863,64.131 A 126.812,126.812 0 0 1 63.305,19.394"
|
||||
id="path1" />
|
||||
<path
|
||||
d="m 232.123,79.636 c 1.899,6.44 1.558,11.76 -1.522,14.834 -7.193,7.196 -26.624,-0.464 -44.14,-17.134 a 89.383,89.383 0 0 1 -3.627,-3.428 C 176.5,67.572 171.572,60.828 168.42,54.617 c -6.135,-11.006 -7.67,-20.726 -3.033,-25.364 2.527,-2.524 6.57,-3.212 11.502,-2.325 3.216,-2.034 7.013,-4.3 11.176,-6.621 C 171.136,11.477 151.889,6.49 131.472,6.49 63.753,6.49 8.854,61.38 8.854,129.105 c 0,67.713 54.9,122.61 122.618,122.61 67.72,0 122.616,-54.897 122.616,-122.61 0,-21.87 -5.74,-42.377 -15.767,-60.156 -2.167,3.955 -4.274,7.578 -6.198,10.687"
|
||||
id="path2" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
191
docs/site/static/static/images/logo-linux.svg
Normal file
@@ -0,0 +1,191 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 64.000002 63.999997"
|
||||
version="1.1"
|
||||
id="svg29"
|
||||
sodipodi:docname="logo-linux.svg"
|
||||
width="64"
|
||||
height="64"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview29"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="8"
|
||||
inkscape:cx="-6.375"
|
||||
inkscape:cy="56.625"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1367"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg29" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<style
|
||||
id="style1">.c{fill:#010101;}.d{fill:#e8ab24;}.e{fill:#fff;}.f{fill:#ffe02c;}.g{fill:#fbdd99;}.h{fill:#fece33;}.i{fill:#202121;}.j{fill:#4a4a4c;}.k{fill:#d8d8d8;mix-blend-mode:multiply;}.l{isolation:isolate;}</style>
|
||||
</defs>
|
||||
<g
|
||||
class="l"
|
||||
id="g29"
|
||||
style="isolation:isolate"
|
||||
transform="matrix(0.1040442,0,0,0.1040442,0.0140941,-5.1112806)">
|
||||
<g
|
||||
id="a" />
|
||||
<g
|
||||
id="b">
|
||||
<g
|
||||
id="g28">
|
||||
<path
|
||||
class="i"
|
||||
d="m 220.98,553.96 c 7.52,-0.84 29.24,3.34 27.72,-19.22 -0.16,-2.42 -0.87,-4.82 -2,-7.2 -5.1,-10.72 -19.18,-21.23 -34.94,-33.23 -3.54,-2.69 -7.16,-5.47 -10.78,-8.33 -10.07,-7.96 -20.15,-16.64 -28.45,-26.47 -27.57,-32.63 -7.52,-65.96 -7.52,-65.96 -5.01,45.95 17.55,58.48 17.55,58.48 -7.52,-27.57 16.71,-91.07 40.1,-126.21 19.41,-29.15 21.02,-63.91 21.01,-74.94 -1.37,-2.27 -2.55,-4.65 -3.5,-7.14 -2.04,-2.76 -3.77,-5.72 -4.98,-8.95 -0.52,-1.38 -0.86,-2.7 -1.08,-3.99 -0.15,-0.99 -0.25,-1.95 -0.27,-2.84 0,-0.04 -0.01,-0.09 -0.01,-0.13 v 0 c -0.07,-9.83 8.16,-16.96 16.33,-22.66 -4.82,-0.42 -9.98,-2.68 -13.85,-9.1 -9.74,-16.16 -4.88,-59.87 20.18,-57.37 25.06,2.51 21.34,48.09 21.34,48.09 6.02,-2.97 10.83,-4.64 10.83,-4.64 4.22,-0.34 7.51,-0.32 10.52,0.12 v 0 c 0,0 -3.42,-48.16 32.5,-48.16 35.92,0 30.13,67.3 19.24,75.43 v 0 c 5.85,4.16 8.89,8.22 9.88,12.15 0.24,0.94 0.33,1.87 0.35,2.79 1.08,0.52 1.63,1.37 1.63,3.36 0,3.81 -1.79,8.49 -4.61,13.38 -0.66,15.07 3.51,44.66 40.77,102.19 23.76,36.69 30.63,67.14 31.55,88.45 3.84,-0.96 7.77,-1.37 11.71,-1.33 10.9,-24.25 -9.01,-80.88 -9.01,-80.88 21.82,24.1 22.07,73.15 21.9,82.66 9.21,2.43 17.92,7.16 24.88,13.44 17.13,15.46 10.04,25.67 10.04,25.67 v 0 c 6.24,0.17 12.36,1.83 18.61,6.11 0.32,0.22 0.58,0.47 0.89,0.7 4.2,-19.86 5.87,-39.12 5.27,-55.6 -2.23,-61.4 -45.67,-98.16 -65.73,-123.78 C 429,273.23 407.83,256.52 406.72,175.2 405.61,93.88 371.07,50.43 308.69,51.55 c -62.38,1.11 -97.36,31.97 -89.12,140.01 5.57,101.37 -36.76,117.32 -64.61,155.19 -13.66,18.58 -34.01,65.59 -32.41,118 3.51,-1.44 8.28,-2.41 14.86,-2.7 36.48,-1.58 40.94,35.93 83.55,91.9 z M 323.37,69.66 c 0,0 -60.14,11.78 -81.87,38.6 0,0 14.07,-49.08 81.87,-38.6 z M 181.98,330.73 c 33.97,-31.27 38.5,-74.12 38.5,-74.12 8.78,29.91 -14.5,76.28 -33.4,86.06 -13.33,6.91 -39.38,35.58 -39.38,35.58 0,0 6.35,-21.81 34.28,-47.53 z"
|
||||
id="path1"
|
||||
style="fill:#202121" />
|
||||
<path
|
||||
class="i"
|
||||
d="m 377.47,593.68 c 0.12,-7.54 0.32,-14.65 0.6,-21.37 -63.97,38.15 -116.29,18.95 -142.4,3.57 12.75,22.27 13.99,37.1 12.57,45.82 26.8,2.57 80.13,5.67 129.65,-3.35 -0.39,-6.95 -0.57,-15.07 -0.42,-24.68 z"
|
||||
id="path2"
|
||||
style="fill:#202121" />
|
||||
<path
|
||||
class="e"
|
||||
d="m 242.09,169.77 c 1.45,-8.56 19.37,-16.16 23.25,0.55 2.88,12.4 1.86,19.8 0.9,23.34 3.78,-2.72 7.91,-5.03 11.6,-6.85 0,0 3.72,-45.58 -21.34,-48.09 -25.06,-2.51 -29.92,41.21 -20.18,57.37 3.87,6.42 9.03,8.67 13.85,9.1 1.93,-1.35 3.84,-2.61 5.65,-3.82 -13.86,-3.28 -15.51,-23.16 -13.72,-31.59 z"
|
||||
id="path3"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="e"
|
||||
d="m 319.15,162.53 c 2.51,-3.76 16.29,-11.28 21.3,6.68 5,17.92 -5.76,30.79 -5.81,30.85 3.03,1.73 6.28,3.56 9.99,5.59 2.39,1.31 4.46,2.61 6.28,3.9 v 0 c 10.89,-8.13 16.69,-75.43 -19.24,-75.43 -35.93,0 -32.5,48.15 -32.5,48.15 v 0 c 5.41,0.79 9.98,2.99 17.33,7.23 -4.87,-14.91 0.41,-23.61 2.64,-26.97 z"
|
||||
id="path4"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="c"
|
||||
d="m 340.46,169.21 c -5.01,-17.96 -18.8,-10.44 -21.3,-6.68 -2.24,3.36 -7.51,12.06 -2.64,26.97 4.61,2.66 10.38,6.14 18.14,10.56 0.05,-0.06 10.81,-12.92 5.81,-30.85 z m -20.45,-1.92 c 0,0 9.68,-12.63 18.32,1.67 0,0 -8.83,-4.81 -18.32,-1.67 z"
|
||||
id="path5"
|
||||
style="fill:#010101" />
|
||||
<path
|
||||
class="i"
|
||||
d="m 359.24,231.73 c -0.32,2.23 -0.88,5.3 -1.07,9.49 2.82,-4.89 4.61,-9.56 4.61,-13.38 0,-1.99 -0.55,-2.83 -1.63,-3.36 0.04,2.47 -0.66,4.89 -1.92,7.24 v 0 z"
|
||||
id="path6"
|
||||
style="fill:#202121" />
|
||||
<path
|
||||
class="k"
|
||||
d="m 359.24,231.73 c -0.32,2.23 -0.88,5.3 -1.07,9.49 2.82,-4.89 4.61,-9.56 4.61,-13.38 0,-1.99 -0.55,-2.83 -1.63,-3.36 0.04,2.47 -0.66,4.89 -1.92,7.24 v 0 z"
|
||||
id="path7"
|
||||
style="mix-blend-mode:multiply;fill:#d8d8d8" />
|
||||
<path
|
||||
class="i"
|
||||
d="m 240.17,243.75 c 0.95,2.49 2.13,4.87 3.5,7.14 0,-1.35 -0.02,-2.38 -0.04,-2.96 -1.25,-1.33 -2.38,-2.73 -3.45,-4.18 z"
|
||||
id="path8"
|
||||
style="fill:#202121" />
|
||||
<path
|
||||
class="k"
|
||||
d="m 240.17,243.75 c 0.95,2.49 2.13,4.87 3.5,7.14 0,-1.35 -0.02,-2.38 -0.04,-2.96 -1.25,-1.33 -2.38,-2.73 -3.45,-4.18 z"
|
||||
id="path9"
|
||||
style="mix-blend-mode:multiply;fill:#d8d8d8" />
|
||||
<path
|
||||
class="e"
|
||||
d="m 282.72,284.46 c 12.95,8.3 31.75,-1.67 49.29,-14.2 8.18,-5.84 19.79,-17.99 26.16,-29.03 0.18,-4.19 0.74,-7.26 1.07,-9.49 v 0 c -5.12,9.54 -19.28,18 -28.8,24.79 -17.55,12.53 -34.15,20.5 -48.2,14.2 -10.16,-3.88 -27.49,-10.95 -38.61,-22.8 0.02,0.58 0.04,1.61 0.04,2.96 9.57,15.9 28.14,26.58 39.06,33.57 z"
|
||||
id="path10"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="k"
|
||||
d="m 282.72,284.46 c 12.95,8.3 31.75,-1.67 49.29,-14.2 8.18,-5.84 19.79,-17.99 26.16,-29.03 0.18,-4.19 0.74,-7.26 1.07,-9.49 v 0 c -5.12,9.54 -19.28,18 -28.8,24.79 -17.55,12.53 -34.15,20.5 -48.2,14.2 -10.16,-3.88 -27.49,-10.95 -38.61,-22.8 0.02,0.58 0.04,1.61 0.04,2.96 9.57,15.9 28.14,26.58 39.06,33.57 z"
|
||||
id="path11"
|
||||
style="mix-blend-mode:multiply;fill:#d8d8d8" />
|
||||
<path
|
||||
class="c"
|
||||
d="m 265.34,170.32 c -3.88,-16.71 -21.8,-9.12 -23.25,-0.55 -1.79,8.44 -0.13,28.32 13.72,31.59 2.68,-1.78 5.12,-3.43 6.94,-5 1.1,-0.94 2.27,-1.84 3.48,-2.71 0.96,-3.54 1.98,-10.93 -0.9,-23.34 z m -20.49,-0.97 c 0,0 9.68,-12.63 18.32,1.67 0,0 -8.83,-4.81 -18.32,-1.67 z"
|
||||
id="path12"
|
||||
style="fill:#010101" />
|
||||
<path
|
||||
class="i"
|
||||
d="m 419,442.84 c 0,0 -0.64,9.61 1.73,20.19 2.4,0.77 4.36,1.8 5.62,2.4 12.11,5.85 14.62,17.13 33,12.11 9.58,-2.61 18.25,-5.43 26.61,-6.01 v 0 c 9.07,-17 -37.71,-51.25 -66.96,-28.69 z"
|
||||
id="path13"
|
||||
style="fill:#202121" />
|
||||
<path
|
||||
class="e"
|
||||
d="m 244.85,169.35 c 9.5,-3.14 18.32,1.67 18.32,1.67 -8.64,-14.3 -18.32,-1.67 -18.32,-1.67 z"
|
||||
id="path14"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="e"
|
||||
d="m 320.01,167.29 c 9.49,-3.14 18.32,1.67 18.32,1.67 -8.64,-14.3 -18.32,-1.67 -18.32,-1.67 z"
|
||||
id="path15"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="h"
|
||||
d="m 244.07,235.79 0.96,-1.26 c 0.24,0.18 24.07,18.13 46.15,15.99 22.37,-2.16 51.25,-14.84 59.8,-22.34 l 1.04,1.19 c 5.92,-4.08 8.93,-7.13 8.77,-7.68 -0.99,-3.93 -4.03,-7.99 -9.88,-12.15 -1.82,-1.29 -3.89,-2.59 -6.28,-3.9 -3.7,-2.03 -6.96,-3.86 -9.99,-5.59 -7.76,-4.42 -13.53,-7.9 -18.14,-10.56 -7.35,-4.24 -11.92,-6.44 -17.33,-7.22 -3.02,-0.44 -6.3,-0.45 -10.52,-0.12 0,0 -4.8,1.67 -10.83,4.64 -3.69,1.82 -7.82,4.13 -11.6,6.85 -1.2,0.86 -2.37,1.76 -3.48,2.71 -1.82,1.56 -4.27,3.22 -6.94,5 -1.81,1.21 -3.72,2.47 -5.65,3.82 -8.17,5.71 -16.4,12.83 -16.33,22.67 0.04,0.05 3.88,3.59 10.24,7.94 z"
|
||||
id="path16"
|
||||
style="fill:#fece33" />
|
||||
<path
|
||||
class="d"
|
||||
d="m 282.24,270.73 c 14.05,6.3 30.65,-1.67 48.2,-14.2 9.51,-6.8 23.67,-15.26 28.8,-24.79 1.26,-2.35 1.96,-4.77 1.92,-7.24 -0.02,-0.92 -0.11,-1.85 -0.35,-2.79 0.16,0.54 -2.85,3.59 -8.77,7.68 l 1.06,1.21 c -8.76,7.68 -37.8,20.82 -61.6,23.11 -1.36,0.13 -2.72,0.19 -4.08,0.19 -21.95,0 -43.35,-16.1 -44.3,-16.83 l 0.97,-1.28 c -6.36,-4.35 -10.2,-7.88 -10.24,-7.94 v 0 c 0,0 0.01,0.08 0.01,0.12 0.02,0.89 0.11,1.84 0.27,2.84 0.22,1.29 0.57,2.61 1.08,3.99 1.21,3.23 2.94,6.19 4.98,8.95 1.07,1.45 2.21,2.85 3.45,4.18 11.13,11.85 28.46,18.92 38.61,22.8 z"
|
||||
id="path17"
|
||||
style="fill:#e8ab24" />
|
||||
<path
|
||||
class="h"
|
||||
d="m 235.66,575.88 c -3.8,-6.64 -8.58,-13.91 -14.68,-21.92 -42.61,-55.98 -47.07,-93.48 -83.55,-91.9 -6.58,0.28 -11.36,1.26 -14.86,2.7 -15.61,6.42 -5.82,22.23 -16.05,29.05 -5.84,3.89 -13.48,4.89 -21.09,6.44 0,0 -23.19,12.77 -10.34,35.05 12.86,22.27 11.96,22.43 7.48,57.29 -4.49,34.85 165.65,29.12 165.65,29.12 v 0 c 1.42,-8.72 0.18,-23.55 -12.57,-45.82 z M 143.47,472.17 c 22.86,8.53 28.1,28.08 28.1,28.08 0,0 -36.13,-38.24 -49.1,-5.34 0,0 -1.86,-31.28 21,-22.74 z"
|
||||
id="path18"
|
||||
style="fill:#fece33" />
|
||||
<path
|
||||
class="d"
|
||||
d="m 82.58,592.58 c 4.49,-34.85 5.38,-35.02 -7.48,-57.29 -12.86,-22.27 10.34,-35.05 10.34,-35.05 -8.72,1.78 -17.39,4.29 -23.19,12.77 -10.86,15.87 -5.01,30.91 0,49.29 5.01,18.38 -6.27,20.89 -12.11,33 -4.01,8.32 -26.38,46.5 128.25,48.88 63.5,0.97 64.12,-11.76 66,-12.53 0,0 2.73,-3.1 3.85,-9.96 v 0 c 0,0 -170.14,5.73 -165.65,-29.12 z"
|
||||
id="path19"
|
||||
style="fill:#e8ab24" />
|
||||
<path
|
||||
class="h"
|
||||
d="m 443.31,627.88 c 38.94,-29.27 119.53,-63.4 89.61,-101.95 -1.95,-0.81 -3.7,-2.05 -5.07,-4.11 -8.19,-12.28 -3.59,-32.55 -18.33,-43.58 -0.3,-0.23 -0.57,-0.48 -0.89,-0.7 -6.25,-4.28 -12.37,-5.94 -18.62,-6.11 -1.35,-0.04 -2.71,0 -4.07,0.09 -8.36,0.58 -17.03,3.4 -26.61,6.01 -18.38,5.01 -20.89,-6.27 -33,-12.11 -1.26,-0.61 -3.21,-1.63 -5.62,-2.4 -12.42,-3.98 -37.07,-1.06 -42.36,102.68 -0.11,2.16 -0.21,4.36 -0.31,6.6 -0.28,6.72 -0.48,13.83 -0.6,21.37 -0.15,9.61 0.03,17.73 0.42,24.68 0.69,12.29 2.08,20.81 3.69,26.72 6.65,2.31 29.29,7.2 61.74,-17.19 z M 415.24,480.7 c 19.55,-3.74 26.43,15.7 26.43,15.7 0,0 -30.26,-22.44 -47.25,40.38 0,0 1.28,-52.35 20.83,-56.09 z"
|
||||
id="path20"
|
||||
style="fill:#fece33" />
|
||||
<path
|
||||
class="d"
|
||||
d="m 532.92,525.93 c 29.92,38.55 -50.67,72.68 -89.61,101.95 -32.45,24.39 -55.09,19.5 -61.74,17.19 3.49,12.82 7.98,13.52 8.42,14.61 0,0 21.72,19.22 77.7,-23.39 14,-10.66 26.85,-18.93 38.21,-25.91 34.08,-20.93 54.87,-30.28 53.69,-57.64 -1.3,-30 -16.75,-22.71 -26.68,-26.81 z"
|
||||
id="path21"
|
||||
style="fill:#e8ab24" />
|
||||
<path
|
||||
class="j"
|
||||
d="m 323.37,69.66 c -67.8,-10.47 -81.87,38.6 -81.87,38.6 21.73,-26.82 81.87,-38.6 81.87,-38.6 z"
|
||||
id="path22"
|
||||
style="fill:#4a4a4c" />
|
||||
<path
|
||||
class="j"
|
||||
d="m 187.08,342.68 c 18.89,-9.79 42.18,-56.15 33.4,-86.06 0,0 -4.52,42.84 -38.5,74.12 -27.93,25.71 -34.28,47.53 -34.28,47.53 0,0 26.05,-28.67 39.38,-35.58 z"
|
||||
id="path23"
|
||||
style="fill:#4a4a4c" />
|
||||
<path
|
||||
class="g"
|
||||
d="m 441.66,496.41 c 0,0 -6.87,-19.44 -26.43,-15.7 -19.55,3.74 -20.83,56.09 -20.83,56.09 17,-62.82 47.25,-40.38 47.25,-40.38 z"
|
||||
id="path24"
|
||||
style="fill:#fbdd99" />
|
||||
<path
|
||||
class="g"
|
||||
d="m 171.56,500.25 c 0,0 -5.23,-19.54 -28.1,-28.08 -22.86,-8.53 -21,22.74 -21,22.74 12.97,-32.9 49.1,5.34 49.1,5.34 z"
|
||||
id="path25"
|
||||
style="fill:#fbdd99" />
|
||||
<path
|
||||
class="i"
|
||||
d="m 291.18,250.53 c -22.08,2.14 -45.91,-15.81 -46.15,-15.99 l -0.96,1.26 -0.97,1.28 c 0.95,0.73 22.35,16.83 44.3,16.83 1.36,0 2.72,-0.06 4.08,-0.19 23.8,-2.29 52.84,-15.43 61.6,-23.11 l -1.06,-1.21 -1.04,-1.19 c -8.55,7.5 -37.43,20.18 -59.8,22.34 z"
|
||||
id="path26"
|
||||
style="fill:#202121" />
|
||||
<path
|
||||
class="e"
|
||||
d="m 479.99,445.76 c -6.97,-6.29 -15.67,-11.01 -24.88,-13.44 0.17,-9.51 -0.08,-58.56 -21.9,-82.66 0,0 19.91,56.63 9.01,80.88 -3.94,-0.05 -7.87,0.37 -11.71,1.33 -0.92,-21.31 -7.79,-51.76 -31.55,-88.45 -37.26,-57.53 -41.43,-87.12 -40.77,-102.19 -6.36,11.04 -17.98,23.19 -26.16,29.03 -17.55,12.53 -36.35,22.5 -49.29,14.2 -10.92,-6.99 -29.49,-17.67 -39.06,-33.57 0.01,11.03 -1.6,45.79 -21.01,74.94 -23.39,35.14 -47.62,98.63 -40.1,126.21 0,0 -22.56,-12.53 -17.55,-58.48 0,0 -20.05,33.32 7.52,65.96 8.31,9.83 18.39,18.51 28.45,26.47 3.62,2.86 7.24,5.64 10.78,8.33 15.76,12 29.84,22.52 34.94,33.23 1.13,2.38 1.84,4.78 2,7.2 1.53,22.56 -20.2,18.38 -27.72,19.22 6.09,8.01 10.87,15.28 14.68,21.92 26.11,15.38 78.43,34.58 142.4,-3.57 0.09,-2.24 0.2,-4.44 0.31,-6.6 5.29,-103.75 29.93,-106.67 42.36,-102.68 -2.37,-10.57 -1.73,-20.19 -1.73,-20.19 29.24,-22.56 76.03,11.7 66.96,28.69 1.36,-0.1 2.72,-0.13 4.07,-0.09 h 0.01 c 0,0 7.08,-10.22 -10.04,-25.68 z"
|
||||
id="path27"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="f"
|
||||
d="m 287.18,192.08 c 12.42,0.93 10.28,45.4 5.32,47.28 -11.9,4.53 -54.16,-13.14 -46.98,-20.28 5.37,-5.34 32.65,-27.67 41.65,-27 z"
|
||||
id="path28"
|
||||
style="fill:#ffe02c" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
272
docs/site/static/static/images/logo-netbsd.svg
Normal file
@@ -0,0 +1,272 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
id="svg3295"
|
||||
version="1.1"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
sodipodi:docname="logo-netbsd.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs3297">
|
||||
<clipPath
|
||||
id="clipPath5208"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path5210"
|
||||
d="m 259.02,672 h 93.96 v 90 h -93.96 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath5168"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path5170"
|
||||
d="m 303.707,751.075 c -0.385,-0.477 -0.979,-0.703 -1.773,-0.457 v 0 c -0.83,0.256 -1.84,0.797 -2.425,1.19 v 0 c 0.492,-2.044 1.17,-4.564 1.716,-6.735 v 0 c 0.134,-0.535 0.356,-1.196 -0.179,-1.328 v 0 c -0.64,-0.161 -1.18,0.409 -1.479,0.687 v 0 c -1.062,0.992 -2.134,2.125 -3.193,2.887 v 0 c -0.066,-0.399 -0.298,-0.868 -0.532,-1.239 v 0 c -0.19,-0.299 -0.545,-0.668 -1.006,-0.731 v 0 c -0.611,-0.086 -1.63,0.116 -2.484,0.227 v 0 c -0.847,0.113 -1.661,0.272 -2.307,0.322 v 0 c 0.368,-0.86 0.833,-2.091 1.243,-3.115 v 0 c 0.156,-0.395 0.391,-0.77 0.355,-1.054 v 0 c -0.116,-0.876 -1.544,-1.096 -2.425,-1.239 v 0 c 0.121,-0.073 0.244,-0.146 0.366,-0.218 v 0 c 4.347,1.13 9.975,2.091 16.548,2.092 v 0 c 6.575,-10e-4 12.203,-0.962 16.549,-2.092 v 0 c 0.123,0.072 0.245,0.145 0.368,0.218 v 0 c -0.883,0.143 -2.312,0.363 -2.427,1.239 v 0 c -0.037,0.284 0.199,0.659 0.355,1.054 v 0 c 0.409,1.024 0.875,2.255 1.243,3.115 v 0 c -0.645,-0.05 -1.46,-0.209 -2.307,-0.322 v 0 c -0.853,-0.111 -1.874,-0.313 -2.485,-0.227 v 0 c -0.461,0.063 -0.815,0.432 -1.003,0.731 v 0 c -0.236,0.371 -0.467,0.84 -0.535,1.239 v 0 c -1.058,-0.762 -2.13,-1.895 -3.192,-2.887 v 0 c -0.299,-0.278 -0.839,-0.848 -1.48,-0.687 v 0 c -0.534,0.132 -0.312,0.793 -0.177,1.328 v 0 c 0.545,2.171 1.222,4.691 1.714,6.735 v 0 c -0.584,-0.393 -1.594,-0.934 -2.424,-1.19 v 0 c -0.795,-0.246 -1.389,-0.02 -1.773,0.457 v 0 c -0.22,0.274 -1.912,3.664 -2.426,4.491 v 0 c -0.515,-0.827 -2.204,-4.217 -2.425,-4.491"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath5156"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path5158"
|
||||
d="m 283.242,759.362 v -21.128 c 4.31,1.679 12.394,4.129 22.89,4.13 v 0 c 10.406,-10e-4 18.44,-2.407 22.776,-4.086 v 0 21.084 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath5144"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path5146"
|
||||
d="m 259.02,672 h 93.96 v 90 h -93.96 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4074"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4076"
|
||||
d="M 0,0 H 792 V 612 H 0 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4070"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4072"
|
||||
d="M 0,0 H 792 V 612 H 0 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4066"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4068"
|
||||
d="m 332.598,243.597 c -2.835,-5.629 -1.151,-9.057 -1.151,-9.057 v 0 c 6.56,-9.318 17.462,-1.405 17.462,-1.405 v 0 h 83.893 c 0,0 10.903,-7.913 17.462,1.405 v 0 c 0,0 1.684,3.428 -1.15,9.057 v 0 L 390.839,345.59 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4062"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4064"
|
||||
d="M 0,0 H 792 V 612 H 0 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4058"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4060"
|
||||
d="M 0,0 H 792 V 612 H 0 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4054"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4056"
|
||||
d="M 453.785,320.137 H 328.907 c -0.76,0 -1.615,-0.151 -2.566,-0.454 v 0 c -0.949,-0.306 -1.73,-0.683 -2.335,-1.142 v 0 l -6.155,-5.362 c -0.607,-0.381 -1.122,-1.008 -1.536,-1.883 v 0 c -0.42,-0.873 -0.629,-1.691 -0.629,-2.448 v 0 -7.189 c 0,-0.765 0.209,-1.562 0.629,-2.396 v 0 c 0.414,-0.837 0.929,-1.484 1.536,-1.94 v 0 l 6.155,-5.359 c 0.605,-0.382 1.386,-0.743 2.335,-1.083 v 0 c 0.951,-0.347 1.806,-0.518 2.566,-0.518 v 0 h 7.975 9.996 l 113.197,12.587 c 0,0 12.042,4.874 5.136,14.271 v 0 c 0,0 -2.74,2.916 -11.39,2.916 v 0 c -0.012,0 -0.023,0 -0.036,0"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4050"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4052"
|
||||
d="M 0,0 H 792 V 612 H 0 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4046"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4048"
|
||||
d="m 444.482,289.331 h -11.643 l -111.55,-12.586 c 0,0 -12.046,-4.875 -5.136,-14.272 v 0 c 0,0 2.747,-2.929 11.424,-2.919 v 0 h 124.879 c 0.758,0 1.611,0.152 2.561,0.458 v 0 c 0.953,0.306 1.733,0.683 2.34,1.141 v 0 l 6.151,5.361 c 0.611,0.384 1.122,1.007 1.539,1.881 v 0 c 0.421,0.878 0.629,1.694 0.629,2.455 v 0 7.185 c 0,0.765 -0.208,1.561 -0.629,2.399 v 0 c -0.417,0.837 -0.928,1.481 -1.539,1.938 v 0 l -6.151,5.362 c -0.607,0.381 -1.387,0.739 -2.34,1.082 v 0 c -0.95,0.342 -1.803,0.515 -2.561,0.515 v 0 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4042"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4044"
|
||||
d="M 0,0 H 792 V 612 H 0 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4038"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4040"
|
||||
d="M 0,0 H 792 V 612 H 0 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4034"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<path
|
||||
id="path4036"
|
||||
d="m 253.717,286.27 c 0,-75.678 61.345,-137.026 137.024,-137.026 v 0 c 75.675,0 137.023,61.348 137.023,137.026 v 0 c 0,75.675 -61.348,137.021 -137.023,137.021 v 0 c -75.679,0 -137.024,-61.346 -137.024,-137.021"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2999">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="M 461.9,268.95 H 612 V 792 H 461.9 Z"
|
||||
id="path3001" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3013">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="M 539.04,45.84 H 579 v 47.924 h -39.96 z"
|
||||
id="path3015" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3027">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="M 539.041,45.84 H 579 v 47.924 h -39.959 z"
|
||||
id="path3029" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath8695">
|
||||
<path
|
||||
id="path8697"
|
||||
d="m -33.024,-84.5 v 42.915 H -14.74 V -84.5 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="83.625"
|
||||
inkscape:cy="50.375"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g8"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1367"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata3300">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-773.36218)">
|
||||
<g
|
||||
id="g3613"
|
||||
transform="matrix(0.97277831,0,0,0.97277831,5.682844,741.90593)">
|
||||
<g
|
||||
id="g6"
|
||||
style="stroke-width:0.400521;stroke-miterlimit:10"
|
||||
transform="matrix(0.85013793,0,0,0.83216122,-5.8959909,-384.90121)">
|
||||
<g
|
||||
id="g8"
|
||||
style="stroke-width:0.400521"
|
||||
transform="matrix(0.01750061,0,0,-0.01750061,-0.01381586,566.83014)">
|
||||
<path
|
||||
id="path10"
|
||||
style="fill:#777777;stroke:none;stroke-width:0.421865"
|
||||
d="m 913.00026,3026.3896 c 0,0 -12.92387,30.061 -49.36772,9.9431 -30.30316,-16.8105 -14.54488,-47.851 -14.54488,-47.851 L 1418.5901,1784.2129 h 138.1495 L 912.99078,3026.3896"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path12"
|
||||
style="fill:#777777;stroke:none;stroke-width:0.421865"
|
||||
d="m 2529.5901,-93.18323 -430.8379,831.47786 h -185.537 l 432.7864,-915.10837 c 0,0 43.6167,-86.12733 137.2016,-38.51829 93.5532,47.6084 46.4079,122.14986 46.4079,122.14986"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path14"
|
||||
style="fill:#f26711;stroke:none;stroke-width:0.421865"
|
||||
d="m 4117.5937,3108.9044 c -539.9587,259.1515 -1028.4853,135.3899 -1574.8796,-28.1228 -550.0598,-164.524 -1039.3342,-253.5691 -1588.97257,-29.3131 59.87957,-106.9932 89.85617,-160.4793 149.76727,-267.3883 55.0028,-98.3457 110.0794,-196.6809 165.1033,-294.9844 59.9006,-106.9722 89.8456,-160.4161 149.7568,-267.3672 437.621,-123.6879 800.0371,36.7177 1188.9224,231.134 466.9868,233.3248 888.7979,443.235 1422.3526,321.6116 -450.903,225.7939 -859.6639,169.8325 -1331.1377,41.921 461.594,234.989 896.1815,417.756 1419.0875,292.5092"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path16"
|
||||
style="fill:#f26711;stroke:none;stroke-width:0.421865"
|
||||
d="m 920.1205,1186.7234 c 0,-90.2038 0,-177.5531 2.7691,-223.39224 -9.68922,-16.5788 -48.53561,-31.9147 -81.81959,-31.9147 -1.39982,0 -47.18635,70.78104 -112.37448,151.28404 l -320.46356,396.806 c -79.04838,99.9046 -126.22631,156.7612 -151.19982,177.5848 -6.92011,-13.8508 -6.92011,-37.4235 -6.92011,-88.7714 v -269.2104 c 0,-112.3861 5.52977,-216.409 20.80248,-253.9062 12.49097,-29.1129 44.3846,-37.4444 80.44925,-44.3435 l 38.84535,-5.58238 c 11.07956,-11.1228 8.32099,-40.27776 0,-48.52506 -74.91,2.707 -130.39733,4.1605 -185.88467,4.1605 -51.32578,0 -102.64229,-1.4535 -155.348937,-4.1605 -8.330999,8.2473 -11.101574,37.40226 0,48.52506 l 23.572426,4.171 c 37.454791,8.33148 63.797791,16.64198 76.278651,45.75488 13.87183,37.4972 19.43321,141.5201 19.43321,253.9062 v 352.4414 c 0,76.2793 0,98.4616 -9.71029,117.8948 -9.72187,23.5937 -30.52435,37.4866 -79.080717,47.1769 l -38.824279,5.5719 c -9.710494,11.1017 -8.330789,44.3962 5.529353,48.5356 65.208563,-2.7069 134.556883,-4.1394 191.456683,-4.1394 47.15475,0 87.39043,1.3903 116.52444,4.1499 13.8929,-62.4285 104.05455,-167.8418 228.89051,-316.3346 L 699.571,1417.0359 c 62.43905,-72.1504 101.2728,-120.665 133.19909,-152.6218 5.53925,13.9034 5.53925,37.5182 5.53925,62.4391 v 191.4987 c 0,112.365 -5.53925,216.43 -20.82355,253.9482 -12.49097,29.0919 -42.99531,37.3813 -80.44926,44.3225 l -37.46553,5.5719 c -11.08061,11.1017 -8.31046,40.2252 0,48.5356 74.93002,-2.7069 129.02806,-4.1394 185.90573,-4.1394 51.30472,0 101.26227,1.3903 155.37077,4.1499 8.3305,-8.3209 11.0796,-37.4444 0,-48.5672 l -24.9734,-4.1394 c -36.05311,-8.3526 -62.42855,-16.642 -74.93005,-45.8076 -15.23058,-37.4445 -20.80143,-141.5095 -20.80143,-253.9167 v -331.6073"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path18"
|
||||
style="fill:#f26711;stroke:none;stroke-width:0.421865"
|
||||
d="m 1249.7055,1340.7461 c 19.4543,0 41.6155,2.8122 56.9093,12.4709 6.9306,4.2448 9.6692,18.1271 9.6692,31.8936 0,45.8076 -26.3323,76.3531 -73.4986,76.3531 -58.2786,0 -108.2045,-55.4979 -108.2045,-99.873 0,-19.4648 19.4016,-20.8446 63.8083,-20.8446 z m -91.5309,-61.0382 c -31.9042,0 -34.6954,-2.7491 -34.6954,-24.9525 0,-104.0861 66.5995,-205.36 195.596,-205.36 38.8559,0 91.573,8.2894 129.0175,72.1399 15.2622,2.7596 36.0752,-8.3315 36.0752,-31.8726 -56.8882,-117.95796 -159.5313,-158.24624 -242.7729,-158.24624 -187.276,0 -288.52771,131.86134 -288.52771,283.09274 0,174.8251 126.23571,310.8154 299.62941,310.8154 144.2586,0 221.9493,-92.9634 221.9493,-199.8407 0,-26.3533 -6.9306,-45.776 -49.9154,-45.776 h -266.356"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path20"
|
||||
style="fill:#f26711;stroke:none;stroke-width:0.421865"
|
||||
d="m 1747.975,1430.9182 c -18.0218,0 -19.4122,-1.3482 -19.4122,-31.9147 v -208.0564 c 0,-77.7433 0,-163.7761 97.1029,-163.7761 19.4226,0 41.6049,9.7008 58.2785,20.792 13.8403,-4.1289 22.1718,-19.3806 19.3806,-36.0436 -38.8348,-41.60496 -113.7343,-80.50294 -199.7355,-80.50294 -115.1562,0 -155.3708,66.62056 -155.3708,158.24624 v 309.3408 c 0,29.1762 -1.3904,31.9147 -26.3639,31.9147 h -42.9847 c -15.2728,5.6562 -19.4333,33.3261 -8.3421,43.0691 42.9953,15.2095 81.8406,40.2251 115.1457,62.3969 24.963,18.0218 59.6583,47.1347 102.6537,108.2782 9.6902,5.5087 34.6848,4.0868 41.626,-6.9728 V 1533.614 c 0,-26.3428 1.3903,-27.7331 26.3533,-27.7331 h 123.4772 c 6.9307,-5.5298 11.0806,-13.9034 11.0806,-26.3744 0,-15.2622 -4.1499,-40.1935 -18.0218,-48.5883 h -124.8781"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path22"
|
||||
style="fill:#777777;stroke:none;stroke-width:0.421865"
|
||||
d="m 2248.9514,1185.3752 c 0,-108.2361 13.8824,-176.2049 122.108,-176.2049 102.6536,0 156.7401,79.0599 156.7401,191.4671 0,120.7176 -69.3697,205.3284 -202.5161,205.3284 -76.3214,0 -76.3214,-1.3693 -76.3214,-56.9093 v -163.6602 z m 0,320.5057 c 0,-36.1068 1.4114,-38.8243 73.5408,-38.8243 119.2956,0 163.6918,76.2582 163.6918,159.5208 0,120.7071 -74.9205,180.376 -170.6436,180.376 -65.1776,0 -66.589,-11.1438 -66.589,-72.1715 z m -191.446,134.6 c 0,140.0981 -2.7702,155.3287 -61.0276,162.312 l -37.4656,5.5824 c -12.4815,6.9412 -15.2622,48.5462 5.5614,49.9365 105.4343,6.9201 206.6977,12.5026 345.4476,12.5026 133.1464,0 221.9704,-15.2938 282.9664,-51.3795 59.6584,-36.0647 95.702,-94.3643 95.702,-176.1734 0,-116.5781 -98.4721,-162.3541 -141.4674,-173.4874 -13.8929,-2.7175 -27.7753,-8.3104 -27.7753,-15.2411 0,-4.15 6.9728,-8.2789 19.4648,-11.1017 109.5843,-23.6042 202.5056,-95.6809 203.9065,-230.2492 1.4008,-127.6693 -74.8995,-202.6109 -162.2699,-233.16686 -87.4337,-30.51378 -191.4461,-34.67429 -276.0779,-34.67429 -49.926,0 -102.6432,5.60351 -142.8894,5.60351 -68.0004,0 -135.9692,-1.443 -215.0187,-4.1921 -11.0806,8.3105 -11.0806,44.36456 0,48.52506 l 40.2357,8.34198 c 62.4075,12.471 70.739,22.1929 70.739,147.071 v 489.7905"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path24"
|
||||
style="fill:#777777;stroke:none;stroke-width:0.421865"
|
||||
d="m 3045.3547,928.69897 c -127.5956,0 -206.6871,36.07517 -235.8001,52.69607 -26.3533,30.52436 -44.3751,130.41836 -44.3751,224.78266 9.6903,18.0113 41.6155,19.4332 52.7067,4.171 27.7331,-90.1827 120.6544,-217.82038 238.5913,-217.82038 102.6326,0 149.8305,70.74948 149.8305,140.09808 0,112.3756 -105.4133,178.9751 -188.6653,220.5906 -99.8836,50.0102 -208.0881,133.1991 -209.4468,270.6008 0,156.7612 120.6965,265.0289 321.8328,265.0289 45.7655,0 102.6326,-5.5824 158.1516,-22.256 18.0218,-5.5403 30.4927,-8.2789 47.1558,-11.0701 11.049,-30.5243 24.9735,-105.4554 24.9735,-198.4293 -6.9517,-16.6104 -41.6366,-18.0218 -54.1075,-4.2027 -23.5937,70.8127 -83.2732,172.1077 -183.1462,172.1077 -91.5309,0 -141.4568,-59.69 -141.4568,-129.0176 0,-63.8504 56.8144,-122.0868 126.1841,-158.1726 l 91.5942,-48.6093 c 85.9906,-45.776 195.596,-126.2263 195.596,-278.8481 0,-170.6751 -140.1614,-281.64973 -349.6187,-281.64973"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path26"
|
||||
style="fill:#777777;stroke:none;stroke-width:0.421865"
|
||||
d="m 3699.6692,1202.0593 c 0,-76.353 2.7912,-126.2789 27.7331,-155.434 25.0156,-27.7647 65.2092,-40.2357 115.1562,-40.2357 239.9711,0 321.8329,213.6178 321.8329,402.3569 0,249.7562 -131.7877,398.2069 -382.8921,398.2069 -37.3918,0 -63.8083,-6.9412 -72.1188,-18.085 -8.3315,-11.0701 -9.7113,-37.4234 -9.7113,-80.4082 v -506.4219 z m -198.3557,439.8014 c 0,131.7982 -2.7806,158.1515 -66.5995,165.0927 l -26.3428,2.7491 c -13.8508,6.9412 -13.8508,47.219 2.7912,48.6093 116.5255,6.9201 224.709,12.5026 371.7799,12.5026 113.7448,0 227.4686,-11.186 323.2126,-45.8181 181.7453,-65.1987 281.5867,-228.9222 281.5867,-402.3253 0,-185.9584 -88.7713,-335.8311 -248.2816,-414.8911 -94.3222,-47.20834 -208.1091,-65.1775 -350.9879,-65.1775 -65.1671,0 -134.6,8.34206 -184.5049,8.34206 -66.6101,0 -137.3596,-1.443 -216.3985,-4.1921 -11.0911,8.3105 -11.0911,41.59436 0,48.52506 l 42.9953,6.94118 c 62.418,11.0806 70.7495,20.7919 70.7495,148.4718 v 491.1703"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<style
|
||||
id="style4"
|
||||
type="text/css" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
2825
docs/site/static/static/images/logo-openbsd.svg
Normal file
|
After Width: | Height: | Size: 110 KiB |
40
docs/site/static/static/images/logo-windows.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="64"
|
||||
width="64"
|
||||
fill="#0078d6"
|
||||
viewBox="-71.85375 -26.825 33.212401 12.876"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="logo-windows.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="35.57631"
|
||||
inkscape:cy="29.565902"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1367"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
d="m -39.004475,-36.899628 -18.105279,2.641859 v 13.119817 h 18.104665 z m -32.528192,4.745014 v 11.016662 h 13.146875 V -34.07236 Z m 0,23.4486375 13.146875,1.939565 V -19.862837 h -13.146875 z m 32.528192,4.7988121 V -19.862837 h -18.105279 v 13.2852176 z"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="cccccccccccccccccccc"
|
||||
style="stroke-width:0.307479" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -88,7 +88,8 @@ img.box {
|
||||
|
||||
figcaption {
|
||||
color: #888;
|
||||
font-size: 0.9em;
|
||||
font-size: 0.675em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.button {
|
||||
@@ -200,19 +201,56 @@ nav {
|
||||
#download .install-steps li {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
#download .download-links a:not(:last-child) {
|
||||
#download .download-links a {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
font-size: 0.775rem;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
}
|
||||
#download .box {
|
||||
min-height: 630px;
|
||||
#download .download-links a:hover .icon {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
#download .download-links .icon {
|
||||
transition: transform 100ms ease-in;
|
||||
background-color: #fff;
|
||||
box-shadow: 1px 1px 4px #eee;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 100%;
|
||||
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
#download .download-links img {
|
||||
max-width: 24px;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.feature {
|
||||
#download .box {
|
||||
min-height: 475px;
|
||||
}
|
||||
|
||||
.hosting {
|
||||
text-align: center;
|
||||
}
|
||||
.hosting a {
|
||||
display: inline-block;
|
||||
margin: 0 15px;
|
||||
}
|
||||
.hosting .disclaimer {
|
||||
font-size: 0.775rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.feature h2 {
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
.feature img {
|
||||
margin-bottom: 1em;
|
||||
@@ -277,4 +315,7 @@ nav {
|
||||
.container {
|
||||
padding: 0 15px;
|
||||
}
|
||||
.feature .col-1 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -817,7 +817,7 @@ paths:
|
||||
page:
|
||||
type: integer
|
||||
delete:
|
||||
description: handles retrieval of bounce records.
|
||||
description: handles deletion of bounce records.
|
||||
operationId: deleteBounces
|
||||
tags:
|
||||
- Bounces
|
||||
@@ -889,7 +889,7 @@ paths:
|
||||
properties:
|
||||
data:
|
||||
type: boolean
|
||||
|
||||
|
||||
/lists:
|
||||
get:
|
||||
description: retrieves lists with additional metadata like subscriber counts. This may be slow.
|
||||
@@ -1134,8 +1134,8 @@ paths:
|
||||
|
||||
/import/subscribers/logs:
|
||||
get:
|
||||
description: returns import statistics
|
||||
operationId: getImportSubscriberStats
|
||||
description: returns import logs from an ongoing import
|
||||
operationId: getImportSubscriberLogs
|
||||
tags:
|
||||
- Import
|
||||
responses:
|
||||
@@ -1769,6 +1769,28 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Template"
|
||||
post:
|
||||
description: handles template creation
|
||||
operationId: createTemplate
|
||||
tags:
|
||||
- Templates
|
||||
requestBody:
|
||||
description: new template info
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NewTemplate"
|
||||
responses:
|
||||
"200":
|
||||
description: response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/components/schemas/Template"
|
||||
|
||||
"/templates/{id}":
|
||||
get:
|
||||
@@ -1799,7 +1821,35 @@ paths:
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/components/schemas/Template"
|
||||
|
||||
put:
|
||||
description: handles template modification
|
||||
operationId: updateTemplateById
|
||||
tags:
|
||||
- Templates
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
description: The id value of the template you want to update.
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
description: updated template info
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UpdateTemplate"
|
||||
responses:
|
||||
"200":
|
||||
description: response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/components/schemas/Template"
|
||||
delete:
|
||||
description: handles deletion of templates
|
||||
operationId: deleteTemplateById
|
||||
@@ -1892,7 +1942,7 @@ paths:
|
||||
"/templates/{id}/default":
|
||||
put:
|
||||
description: handles template modification.
|
||||
operationId: updateTemplateById
|
||||
operationId: setDefaultTemplateById
|
||||
tags:
|
||||
- Templates
|
||||
parameters:
|
||||
@@ -2110,6 +2160,10 @@ components:
|
||||
type: string
|
||||
analytics.toDate:
|
||||
type: string
|
||||
bounces.numSelected:
|
||||
type: string
|
||||
bounces.selectAll:
|
||||
type: string
|
||||
bounces.source:
|
||||
type: string
|
||||
bounces.unknownService:
|
||||
@@ -4033,10 +4087,20 @@ components:
|
||||
type: string
|
||||
filename:
|
||||
type: string
|
||||
content_type:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
thumb_url:
|
||||
type: string
|
||||
thumb_uri:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
meta:
|
||||
type: object
|
||||
url:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
|
||||
@@ -4053,11 +4117,59 @@ components:
|
||||
type: string
|
||||
body:
|
||||
type: string
|
||||
body_source:
|
||||
type: string
|
||||
subject:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
is_default:
|
||||
type: boolean
|
||||
|
||||
NewTemplate:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
- body
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: Name of the template
|
||||
type:
|
||||
type: string
|
||||
enum: [campaign, campaign_visual, tx]
|
||||
description: Type of the template
|
||||
subject:
|
||||
type: string
|
||||
description: Subject line for the template (only for tx)
|
||||
body_source:
|
||||
type: string
|
||||
description: JSON source for the email-builder template (only for campaign_visual)
|
||||
body:
|
||||
type: string
|
||||
description: HTML body of the template
|
||||
|
||||
UpdateTemplate:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: Name of the template
|
||||
type:
|
||||
type: string
|
||||
enum: [campaign, campaign_visual, tx]
|
||||
description: Type of the template
|
||||
subject:
|
||||
type: string
|
||||
description: Subject line for the template (only for tx)
|
||||
body_source:
|
||||
type: string
|
||||
description: JSON source for the email-builder template (only for campaign_visual)
|
||||
body:
|
||||
type: string
|
||||
description: HTML body of the template
|
||||
|
||||
TransactionalMessage:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -25,10 +25,12 @@ describe('Campaigns', () => {
|
||||
cy.wait(500);
|
||||
|
||||
cy.get('a[data-cy=btn-attach]').click();
|
||||
cy.get('[data-cy=btn-toggle-upload]').click();
|
||||
cy.get('input[type=file]').attachFile('example.json');
|
||||
cy.get('form[data-cy="upload"] button').click();
|
||||
cy.get('.modal button.is-primary:eq(0)').click();
|
||||
cy.wait(500);
|
||||
cy.get('.modal td[data-label=Name] a.link').click();
|
||||
cy.get('.modal a.thumb-link').click();
|
||||
cy.get('button[data-cy=btn-save]').click();
|
||||
cy.wait(500);
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ describe('Login ', () => {
|
||||
cy.get('tbody tr').should('have.length', 2);
|
||||
cy.get('tbody tr:nth-child(1) [data-cy=btn-edit]').should('exist');
|
||||
cy.get('tbody tr:nth-child(1) [data-cy=btn-delete]').should('exist');
|
||||
cy.get('tbody tr:nth-child(2) [data-cy=btn-edit]').should('not.exist');
|
||||
cy.get('tbody tr:nth-child(2) [data-cy=btn-delete]').should('not.exist');
|
||||
cy.get('tbody tr:nth-child(2) [data-cy=btn-edit]').should('exist');
|
||||
cy.get('tbody tr:nth-child(2) [data-cy=btn-delete]').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||
"terser": "^5.34.1",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.4.18"
|
||||
"vite": "^5.4.20"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
||||
@@ -1496,9 +1496,9 @@ isexe@^2.0.0:
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
|
||||
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
@@ -1988,10 +1988,10 @@ use-sync-external-store@1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
|
||||
integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==
|
||||
|
||||
vite@^5.4.18:
|
||||
version "5.4.18"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.18.tgz#b5af357f9d5ebb2e0c085779b7a37a77f09168a4"
|
||||
integrity sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==
|
||||
vite@^5.4.20:
|
||||
version "5.4.20"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.20.tgz#3267a5e03f21212f44edfd72758138e8fcecd76a"
|
||||
integrity sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==
|
||||
dependencies:
|
||||
esbuild "^0.21.3"
|
||||
postcss "^8.4.43"
|
||||
|
||||
10
frontend/package.json
vendored
@@ -7,7 +7,8 @@
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore src",
|
||||
"prebuild": "eslint --ext .js,.vue --ignore-path .gitignore src"
|
||||
"prebuild": "eslint --ext .js,.vue --ignore-path .gitignore src",
|
||||
"postinstall": "cp node_modules/altcha/dist/altcha.umd.cjs ../static/public/static/altcha.umd.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.8.1",
|
||||
@@ -22,7 +23,7 @@
|
||||
"@codemirror/view": "^6.36.5",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@tinymce/tinymce-vue": "^3",
|
||||
"axios": "^1.8.2",
|
||||
"axios": "^1.12.0",
|
||||
"buefy": "^0.9.25",
|
||||
"bulma": "^0.9.4",
|
||||
"chart.js": "^4.4.1",
|
||||
@@ -42,6 +43,7 @@
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"altcha": "^2.1.0",
|
||||
"@types/js-beautify": "^1.14.3",
|
||||
"@vitejs/plugin-vue2": "^2.3.1",
|
||||
"@vue/eslint-config-airbnb": "^7.0.1",
|
||||
@@ -53,7 +55,7 @@
|
||||
"eslint-plugin-import": "^2.23.3",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"sass": "^1.34.0",
|
||||
"vite": "^5.4.19",
|
||||
"vite": "^5.4.21",
|
||||
"vue-eslint-parser": "^9.3.2",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
},
|
||||
@@ -61,4 +63,4 @@
|
||||
"jackspeak": "2.1.1"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
}
|
||||
@@ -169,6 +169,11 @@ export const getSubscriber = async (id) => http.get(
|
||||
{ loading: models.subscribers },
|
||||
);
|
||||
|
||||
export const getSubscriberActivity = async (id) => http.get(
|
||||
`/api/subscribers/${id}/activity`,
|
||||
{ loading: models.subscribers },
|
||||
);
|
||||
|
||||
export const getSubscriberBounces = async (id) => http.get(
|
||||
`/api/subscribers/${id}/bounces`,
|
||||
{ loading: models.bounces },
|
||||
@@ -189,6 +194,11 @@ export const deleteBounces = async (params) => http.delete(
|
||||
{ params, loading: models.bounces },
|
||||
);
|
||||
|
||||
export const blocklistBouncedSubscribers = async () => http.put(
|
||||
'/api/bounces/blocklist',
|
||||
{ loading: models.bounces },
|
||||
);
|
||||
|
||||
export const createSubscriber = (data) => http.post(
|
||||
'/api/subscribers',
|
||||
data,
|
||||
@@ -532,3 +542,19 @@ export const deleteRole = (id) => http.delete(
|
||||
`/api/roles/${id}`,
|
||||
{ loading: models.userRoles },
|
||||
);
|
||||
|
||||
// TOTP 2FA APIs
|
||||
export const getTOTPQR = (id) => http.get(
|
||||
`/api/users/${id}/twofa/totp`,
|
||||
{ camelCase: true },
|
||||
);
|
||||
|
||||
export const enableTOTP = (id, data) => http.put(
|
||||
`/api/users/${id}/twofa`,
|
||||
data,
|
||||
);
|
||||
|
||||
export const disableTOTP = (id, data) => http.delete(
|
||||
`/api/users/${id}/twofa`,
|
||||
{ data },
|
||||
);
|
||||
|
||||
@@ -665,7 +665,7 @@ body.is-noscroll {
|
||||
/* Tabs */
|
||||
.b-tabs {
|
||||
.tab-content {
|
||||
padding: 30px 20px;
|
||||
padding: 15px;
|
||||
|
||||
border: 1px solid #ddd;
|
||||
border-width: 0 1px 1px 1px;
|
||||
@@ -824,6 +824,13 @@ section.lists {
|
||||
.blocklisted {
|
||||
color: red;
|
||||
}
|
||||
|
||||
td.link-click-url {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.b-table.subscriptions {
|
||||
@@ -848,6 +855,9 @@ section.lists {
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
.blocklisted {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
/* Import page */
|
||||
@@ -947,24 +957,117 @@ section.analytics {
|
||||
/* Media gallery */
|
||||
.media-files {
|
||||
img {
|
||||
max-width: 140px;
|
||||
max-height: 140px;
|
||||
border-radius: 3px;
|
||||
max-width: 170px;
|
||||
max-height: 170px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
.item:hover & {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.thumb.box {
|
||||
background: repeating-conic-gradient(#f0f0f0 0 90deg, #fff 90deg 180deg) 0 0 / 32px 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: $white;
|
||||
box-shadow: 2px 2px 0 #f3f3f3;
|
||||
border: 1px solid #e6e6e6;
|
||||
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 2px 2px 5px #eee;
|
||||
|
||||
.actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ext {
|
||||
|
||||
.thumb-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.thumb-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.thumb-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: $grey-dark;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.file-ext {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.actions {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
.item:hover & {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 10px;
|
||||
|
||||
.filename {
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.9rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 0.75rem;
|
||||
color: $grey-light;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Template form */
|
||||
@@ -986,6 +1089,9 @@ section.analytics {
|
||||
|
||||
/* Settings */
|
||||
.settings {
|
||||
.b-tabs .tab-content {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.30;
|
||||
}
|
||||
@@ -1021,6 +1127,9 @@ section.analytics {
|
||||
margin-right: 5px;
|
||||
user-select: auto;
|
||||
}
|
||||
.timestamp {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.line:hover {
|
||||
background: $white-bis;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<template v-if="l">
|
||||
<span :set="line = splitLine(l)" :key="i" class="line">
|
||||
<span class="timestamp">{{ line.timestamp }} </span>
|
||||
<span class="file">{{ line.file }}: </span>
|
||||
<span v-if="line.file !== '*'" class="file">{{ line.file }}: </span>
|
||||
<span class="log-message">{{ line.message }}</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -19,7 +19,7 @@
|
||||
// Regexp for splitting log lines in the following format to
|
||||
// [timestamp] [file] [message].
|
||||
// 2021/05/01 00:00:00:00 init.go:99: reading config: config.toml
|
||||
const reFormatLine = /^([0-9\s:/]+\.[0-9]{6}) (.+?\.go:[0-9]+):\s(.+)$/;
|
||||
const reFormatLine = /^([0-9\s:/]+\.[0-9]{6}) (.+?\.go:[0-9]+|\*):\s(.+)$/;
|
||||
|
||||
export default {
|
||||
name: 'LogView',
|
||||
|
||||
161
frontend/src/components/SubscriberActivity.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="subscriber-activity">
|
||||
<div v-if="isLoading" class="has-text-centered">
|
||||
<b-loading :active="true" :is-full-page="false" />
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<!-- Summary Stats -->
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="box has-text-centered">
|
||||
<p class="heading">{{ $t('globals.terms.campaigns') }}</p>
|
||||
<p class="title">{{ activity.campaignViews ? activity.campaignViews.length : 0 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box has-text-centered">
|
||||
<p class="heading">{{ $t('campaigns.views') }}</p>
|
||||
<p class="title">{{ totalViews }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<div class="box has-text-centered">
|
||||
<p class="heading">{{ $t('campaigns.clicks') }}</p>
|
||||
<p class="title">{{ totalClicks }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campaign Views Section -->
|
||||
<div class="section-header mb-4">
|
||||
<h5 class="title is-5">
|
||||
{{ $t('campaigns.views') }}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div v-if="activity.campaignViews && activity.campaignViews.length > 0">
|
||||
<b-table :data="activity.campaignViews" hoverable default-sort="lastViewedAt" default-sort-direction="desc"
|
||||
paginated :per-page="10" :pagination-simple="false" class="campaign-views-table">
|
||||
<b-table-column v-slot="props" field="subject" :label="$tc('globals.terms.campaign', 1)" sortable>
|
||||
<div v-if="props.row.uuid">
|
||||
<router-link :to="{ name: 'campaign', params: { id: props.row.id } }">
|
||||
{{ props.row.subject || props.row.name }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div v-else>
|
||||
<em class="has-text-grey">{{ $t('subscribers.activity.campaignDeleted') }}</em>
|
||||
</div>
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column v-slot="props" field="viewCount" :label="$t('campaigns.views')" sortable numeric>
|
||||
<span class="tag is-light">{{ props.row.viewCount }}</span>
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column v-slot="props" field="lastViewedAt" :label="$t('globals.fields.createdAt')" sortable>
|
||||
<span v-if="props.row.lastViewedAt">
|
||||
{{ $utils.niceDate(props.row.lastViewedAt, true) }}
|
||||
</span>
|
||||
</b-table-column>
|
||||
</b-table>
|
||||
</div>
|
||||
<div v-else class="has-text-centered has-text-grey p-6">
|
||||
<p class="mt-2">{{ $t('globals.messages.emptyState') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Link Clicks Section -->
|
||||
<div class="section-header mb-4 mt-6">
|
||||
<h5 class="title is-5">
|
||||
{{ $t('campaigns.clicks') }}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div v-if="activity.linkClicks && activity.linkClicks.length > 0">
|
||||
<b-table :data="activity.linkClicks" hoverable default-sort="lastClickedAt" default-sort-direction="desc"
|
||||
paginated :per-page="10" :pagination-simple="false" class="link-clicks-table">
|
||||
<b-table-column v-slot="props" field="url" :label="$t('globals.terms.url')" cell-class="link-click-url"
|
||||
sortable>
|
||||
<a :href="props.row.url" target="_blank" rel="noopener noreferrer">
|
||||
{{ props.row.url }}
|
||||
</a>
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column v-slot="props" field="campaignName" :label="$tc('globals.terms.campaign', 1)" sortable>
|
||||
<div v-if="props.row.campaignUuid">
|
||||
<router-link :to="{ name: 'campaign', params: { id: props.row.campaignId } }">
|
||||
{{ props.row.campaignSubject || props.row.campaignName }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div v-else>
|
||||
—
|
||||
</div>
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column v-slot="props" field="clickCount" :label="$t('campaigns.clicks')" sortable numeric>
|
||||
<span class="tag is-light">{{ props.row.clickCount }}</span>
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column v-slot="props" field="lastClickedAt" :label="$t('globals.fields.createdAt')" sortable>
|
||||
<span v-if="props.row.lastClickedAt">
|
||||
{{ $utils.niceDate(props.row.lastClickedAt, true) }}
|
||||
</span>
|
||||
</b-table-column>
|
||||
</b-table>
|
||||
</div>
|
||||
<div v-else class="has-text-centered has-text-grey p-6">
|
||||
<p class="mt-2">{{ $t('globals.messages.emptyState') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
subscriberId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
activity: {
|
||||
campaignViews: [],
|
||||
linkClicks: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
totalViews() {
|
||||
if (!this.activity.campaignViews) return 0;
|
||||
return this.activity.campaignViews.reduce((sum, v) => sum + (v.viewCount || 0), 0);
|
||||
},
|
||||
|
||||
totalClicks() {
|
||||
if (!this.activity.linkClicks) return 0;
|
||||
return this.activity.linkClicks.reduce((sum, c) => sum + (c.clickCount || 0), 0);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.getActivity();
|
||||
},
|
||||
|
||||
methods: {
|
||||
getActivity() {
|
||||
this.isLoading = true;
|
||||
this.$api.getSubscriberActivity(this.subscriberId).then((data) => {
|
||||
this.activity = data;
|
||||
this.isLoading = false;
|
||||
}).catch(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -65,7 +65,9 @@ export default {
|
||||
iframe.contentWindow.EmailBuilder.render('visual-editor-container', {
|
||||
data: {},
|
||||
onChange: (data, body) => {
|
||||
this.$emit('change', { source: JSON.stringify(data), body });
|
||||
// Hack to fix quotes in Go {{ templating }} in the HTML body.
|
||||
const tpl = body.replace(/\{\{[^}]*\}\}/g, (match) => match.replace(/"/g, '"'));
|
||||
this.$emit('change', { source: JSON.stringify(data), body: tpl });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,6 +68,12 @@ async function initConfig(app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the user role has global list permissions, return true.
|
||||
const can = Vue.prototype.$can('lists:get_all', 'lists:manage_all');
|
||||
if (can) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return profile.listRole.lists.some((list) => list.id === id && list.permissions.includes(perm));
|
||||
};
|
||||
|
||||
|
||||
@@ -7,25 +7,43 @@
|
||||
<span v-if="bounces.total > 0">({{ bounces.total }})</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="column has-text-right buttons">
|
||||
<b-button v-if="bulk.checked.length > 0 || bulk.all" type="is-primary" icon-left="trash-can-outline"
|
||||
data-cy="btn-delete" @click.prevent="$utils.confirm(null, () => deleteBounces())">
|
||||
{{ $t('globals.buttons.clear') }}
|
||||
</b-button>
|
||||
<b-button v-if="bounces.total" icon-left="trash-can-outline" data-cy="btn-delete"
|
||||
@click.prevent="$utils.confirm(null, () => deleteBounces(true))">
|
||||
{{ $t('globals.buttons.clearAll') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<b-table :data="bounces.results" :hoverable="true" :loading="loading.bounces" default-sort="createdAt" checkable
|
||||
@check-all="onTableCheck" @check="onTableCheck" :checked-rows.sync="bulk.checked" detailed show-detail-icon
|
||||
paginated backend-pagination pagination-position="both" @page-change="onPageChange" :current-page="queryParams.page" :per-page="bounces.perPage"
|
||||
:total="bounces.total" backend-sorting @sort="onSort">
|
||||
paginated backend-pagination pagination-position="both" @page-change="onPageChange"
|
||||
:current-page="queryParams.page" :per-page="bounces.perPage" :total="bounces.total" backend-sorting
|
||||
@sort="onSort">
|
||||
<template #top-left>
|
||||
<div class="actions">
|
||||
<template v-if="bulk.checked.length > 0">
|
||||
<a class="a" href="#" @click.prevent="$utils.confirm(null, () => deleteBounces())" data-cy="btn-delete">
|
||||
<b-icon icon="trash-can-outline" size="is-small" /> {{ $t('globals.buttons.delete') }}
|
||||
</a>
|
||||
<a class="a" href="#" @click.prevent="$utils.confirm(null, () => blocklistSubscribers())"
|
||||
data-cy="btn-manage-blocklist">
|
||||
<b-icon icon="account-off-outline" size="is-small" /> {{ $t('import.blocklist') }}
|
||||
</a>
|
||||
<span>
|
||||
{{ $t('globals.messages.numSelected', { num: numSelectedBounces }) }}
|
||||
<span v-if="!bulk.all && bounces.total > bounces.perPage">
|
||||
—
|
||||
<a href="#" @click.prevent="selectAllBounces">
|
||||
{{ $t('subscribers.selectAll', { num: bounces.total }) }}
|
||||
</a>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<b-table-column v-slot="props" field="email" :label="$t('subscribers.email')" :td-attrs="$utils.tdID" sortable>
|
||||
<router-link :to="{ name: 'subscriber', params: { id: props.row.subscriberId } }">
|
||||
<router-link :to="{ name: 'subscriber', params: { id: props.row.subscriberId } }"
|
||||
:class="{ 'blocklisted': props.row.subscriberStatus === 'blocklisted' }">
|
||||
{{ props.row.email }}
|
||||
<b-tag v-if="props.row.subscriberStatus !== 'enabled'" :class="props.row.subscriberStatus"
|
||||
data-cy="blocklisted">
|
||||
{{ $t(`subscribers.status.${props.row.subscriberStatus}`) }}
|
||||
</b-tag>
|
||||
</router-link>
|
||||
</b-table-column>
|
||||
|
||||
@@ -119,7 +137,10 @@ export default Vue.extend({
|
||||
this.queryParams.page = p;
|
||||
this.getBounces();
|
||||
},
|
||||
|
||||
// Mark all bounces in the query as selected.
|
||||
selectAllBounces() {
|
||||
this.bulk.all = true;
|
||||
},
|
||||
onTableCheck() {
|
||||
// Disable bulk.all selection if there are no rows checked in the table.
|
||||
if (this.bulk.checked.length !== this.bounces.total) {
|
||||
@@ -149,35 +170,47 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
deleteBounces(all) {
|
||||
const fnSuccess = () => {
|
||||
deleteBounces() {
|
||||
const params = {};
|
||||
if (!this.bulk.all && this.bulk.checked.length > 0) {
|
||||
params.id = this.bulk.checked.map((s) => s.id);
|
||||
} else if (this.bulk.all) {
|
||||
params.all = true;
|
||||
}
|
||||
|
||||
this.$api.deleteBounces(params).then(() => {
|
||||
this.getBounces();
|
||||
this.$utils.toast(this.$t(
|
||||
'globals.messages.deletedCount',
|
||||
{ name: this.$tc('globals.terms.bounces'), num: this.bounces.total },
|
||||
{ name: this.$tc('globals.terms.bounces'), num: this.numSelectedBounces },
|
||||
));
|
||||
});
|
||||
},
|
||||
|
||||
blocklistSubscribers() {
|
||||
const cb = () => {
|
||||
this.getBounces();
|
||||
this.$utils.toast(this.$t('globals.messages.done'));
|
||||
};
|
||||
|
||||
if (all) {
|
||||
this.$api.deleteBounces({ all: true }).then(fnSuccess);
|
||||
if (!this.bulk.all && this.bulk.checked.length > 0) {
|
||||
const subIds = this.bulk.checked.map((s) => s.subscriberId);
|
||||
this.$api.blocklistSubscribers({ ids: subIds }).then(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
const ids = this.bulk.checked.map((s) => s.id);
|
||||
this.$api.deleteBounces({ id: ids }).then(fnSuccess);
|
||||
this.$api.blocklistBouncedSubscribers({ all: true }).then(cb);
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['templates', 'loading']),
|
||||
|
||||
selectedBounces() {
|
||||
numSelectedBounces() {
|
||||
if (this.bulk.all) {
|
||||
return this.bounces.total;
|
||||
}
|
||||
return this.bulk.checked.length;
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
||||
@@ -91,9 +91,15 @@ export default Vue.extend({
|
||||
|
||||
// Captcha?
|
||||
if (this.serverConfig.public_subscription.captcha_enabled) {
|
||||
h += '\n'
|
||||
+ ` <div class="h-captcha" data-sitekey="${this.serverConfig.public_subscription.captcha_key}"></div>\n`
|
||||
+ ` <${'script'} src="https://js.hcaptcha.com/1/api.js" async defer></${'script'}>\n`;
|
||||
if (this.serverConfig.public_subscription.captcha_provider === 'altcha') {
|
||||
h += '\n'
|
||||
+ ` <altcha-widget challengeurl="${this.serverConfig.root_url}/api/public/captcha/altcha"></altcha-widget>\n`
|
||||
+ ` <${'script'} type="module" src="${this.serverConfig.root_url}/public/static/altcha.umd.js" async defer></${'script'}>\n`;
|
||||
} else if (this.serverConfig.public_subscription.captcha_provider === 'hcaptcha') {
|
||||
h += '\n'
|
||||
+ ` <div class="h-captcha" data-sitekey="${this.serverConfig.public_subscription.captcha_key}"></div>\n`
|
||||
+ ` <${'script'} src="https://js.hcaptcha.com/1/api.js" async defer></${'script'}>\n`;
|
||||
}
|
||||
}
|
||||
|
||||
h += '\n'
|
||||
|
||||
@@ -256,8 +256,7 @@ export default Vue.extend({
|
||||
|
||||
getLogs() {
|
||||
this.$api.getImportLogs().then((data) => {
|
||||
this.logs = data.split('\n');
|
||||
|
||||
this.logs = data.split('\n').map((line) => line.replace(/\s+importer\.go:\d+:\s*/, ' *: '));
|
||||
Vue.nextTick(() => {
|
||||
// vue.$refs doesn't work as the logs textarea is rendered dynamically.
|
||||
const ref = document.getElementById('import-log');
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
</b-tooltip>
|
||||
</a>
|
||||
|
||||
<router-link v-if="$can('lists:import')" :to="{ name: 'import', query: { list_id: props.row.id } }"
|
||||
<router-link v-if="$can('subscribers:import')" :to="{ name: 'import', query: { list_id: props.row.id } }"
|
||||
data-cy="btn-import">
|
||||
<b-tooltip :label="$t('import.title')" type="is-dark">
|
||||
<b-icon icon="file-upload-outline" size="is-small" />
|
||||
|
||||
@@ -2,97 +2,108 @@
|
||||
<section class="media-files">
|
||||
<h1 class="title is-4">
|
||||
{{ $t('media.title') }}
|
||||
<span v-if="media.length > 0">({{ media.length }})</span>
|
||||
|
||||
<span class="has-text-grey-light"> / {{ settings['upload.provider'] }}</span>
|
||||
<span v-if="media.results && media.results.length > 0">({{ media.results.length }})</span>
|
||||
<span class="has-text-grey-light"> / {{ serverConfig.media_provider }}</span>
|
||||
</h1>
|
||||
|
||||
<b-loading :active="isProcessing || loading.media" />
|
||||
|
||||
<section class="wrap">
|
||||
<form @submit.prevent="onSubmit" class="box">
|
||||
<div>
|
||||
<b-field :label="$t('media.uploadImage')">
|
||||
<b-upload v-model="form.files" drag-drop multiple xaccept=".png,.jpg,.jpeg,.gif,.svg" expanded>
|
||||
<div class="has-text-centered section">
|
||||
<p>
|
||||
<b-icon icon="file-upload-outline" size="is-large" />
|
||||
</p>
|
||||
<p>{{ $t('media.uploadHelp') }}</p>
|
||||
</div>
|
||||
</b-upload>
|
||||
</b-field>
|
||||
<div class="tags" v-if="form.files.length > 0">
|
||||
<b-tag v-for="(f, i) in form.files" :key="i" size="is-medium" closable @close="removeUploadFile(i)">
|
||||
{{ f.name }}
|
||||
</b-tag>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<b-button native-type="submit" type="is-primary" icon-left="file-upload-outline"
|
||||
:disabled="form.files.length === 0" :loading="isProcessing">
|
||||
{{ $tc('media.upload') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="wrap gallery mt-6">
|
||||
<b-table :data="media.results" :hoverable="true" :loading="loading.media" default-sort="createdAt" :paginated="true"
|
||||
backend-pagination pagination-position="both" @page-change="onPageChange" :current-page="media.page"
|
||||
:per-page="media.perPage" :total="media.total">
|
||||
<template #top-left>
|
||||
<div class="columns">
|
||||
<div class="column is-6">
|
||||
<form @submit.prevent="onQueryMedia">
|
||||
<div>
|
||||
<b-field>
|
||||
<b-input v-model="queryParams.query" name="query" expanded icon="magnify" ref="query"
|
||||
data-cy="query" />
|
||||
<p class="controls">
|
||||
<b-button native-type="submit" type="is-primary" icon-left="magnify" data-cy="btn-query" />
|
||||
</p>
|
||||
</b-field>
|
||||
<div class="columns mb-4">
|
||||
<div class="column">
|
||||
<form @submit.prevent="onQueryMedia" class="search">
|
||||
<div>
|
||||
<b-field>
|
||||
<b-input v-model="queryParams.query" name="query" expanded icon="magnify" ref="query" data-cy="query" />
|
||||
<p class="controls">
|
||||
<b-button native-type="submit" type="is-primary" icon-left="magnify" data-cy="btn-query" />
|
||||
</p>
|
||||
</b-field>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div v-if="$can('media:manage')" class="column is-narrow">
|
||||
<b-button @click="onToggleForm" icon-left="file-upload-outline" data-cy="btn-toggle-upload">
|
||||
{{ $t('media.upload') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-collapse v-if="$can('media:manage')" v-model="showUploadForm" animation="">
|
||||
<form @submit.prevent="onSubmit" class="mb-6" data-cy="upload">
|
||||
<div>
|
||||
<b-field :label="$t('media.upload')">
|
||||
<b-upload v-model="form.files" drag-drop multiple xaccept=".png,.jpg,.jpeg,.gif,.svg" expanded>
|
||||
<div class="has-text-centered section">
|
||||
<p>
|
||||
<b-icon icon="file-upload-outline" size="is-large" />
|
||||
</p>
|
||||
<p>{{ $t('media.uploadHelp') }}</p>
|
||||
</div>
|
||||
</form>
|
||||
</b-upload>
|
||||
</b-field>
|
||||
<div class="tags" v-if="form.files.length > 0">
|
||||
<b-tag v-for="(f, i) in form.files" :key="i" size="is-medium" closable @close="removeUploadFile(i)">
|
||||
{{ f.name }}
|
||||
</b-tag>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<b-button native-type="submit" type="is-primary" icon-left="file-upload-outline"
|
||||
:disabled="form.files.length === 0" :loading="isProcessing">
|
||||
{{ $tc('media.upload') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</form>
|
||||
</b-collapse>
|
||||
|
||||
<b-table-column v-slot="props" field="name" width="40%" :label="$t('globals.fields.name')">
|
||||
<a @click="(e) => onMediaSelect(props.row, e)" :href="props.row.url" target="_blank" rel="noopener noreferer"
|
||||
class="link" :title="props.row.filename">
|
||||
{{ props.row.filename }}
|
||||
</a>
|
||||
</b-table-column>
|
||||
<!-- Pagination -->
|
||||
<div v-if="media.total > media.perPage" class="pagination-wrapper mt-5">
|
||||
<b-pagination :total="media.total" :current.sync="media.page" :per-page="media.perPage"
|
||||
@change="onPageChange" />
|
||||
</div>
|
||||
|
||||
<b-table-column v-slot="props" field="thumb" width="30%">
|
||||
<a @click="(e) => onMediaSelect(props.row, e)" :href="props.row.url" target="_blank" rel="noopener noreferer"
|
||||
class="thumb box">
|
||||
<img v-if="props.row.thumbUrl" :src="props.row.thumbUrl" :title="props.row.filename" alt="" />
|
||||
<span v-else class="ext">
|
||||
{{ props.row.filename.split(".").pop() }}
|
||||
</span>
|
||||
</a>
|
||||
</b-table-column>
|
||||
<div v-if="loading.media" class="has-text-centered py-6">
|
||||
<b-loading :active="loading.media" />
|
||||
</div>
|
||||
<div v-else-if="media.results && media.results.length > 0" class="grid">
|
||||
<div v-for="item in media.results" :key="item.id" class="item">
|
||||
<div class="thumb">
|
||||
<a @click="(e) => onMediaSelect(item, e)" :href="item.url" target="_blank" rel="noopener noreferer"
|
||||
class="thumb-link">
|
||||
<div class="thumb-container">
|
||||
<img v-if="item.thumbUrl" :src="item.thumbUrl" :title="item.filename" :alt="item.filename" />
|
||||
<div v-else class="thumb-placeholder">
|
||||
<span class="file-ext">
|
||||
{{ item.filename.split(".").pop().toUpperCase() }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="actions">
|
||||
<a href="#" @click.prevent="$utils.confirm(null, () => onDeleteMedia(item.id))" data-cy="btn-delete"
|
||||
:aria-label="$t('globals.buttons.delete')" class="delete-btn">
|
||||
<b-icon icon="trash-can-outline" size="is-small" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<p class="filename" :title="item.filename">{{ item.filename }}</p>
|
||||
<p class="date">{{ $utils.niceDate(item.createdAt, false) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-table-column v-slot="props" field="created_at" width="25%" :label="$t('globals.fields.createdAt')" sortable>
|
||||
{{ $utils.niceDate(props.row.createdAt, true) }}
|
||||
</b-table-column>
|
||||
<!-- Empty State -->
|
||||
<div v-else-if="!loading.media">
|
||||
<empty-placeholder />
|
||||
</div>
|
||||
|
||||
<b-table-column v-slot="props" field="actions" width="5%" cell-class="has-text-right">
|
||||
<a href="#" @click.prevent="$utils.confirm(null, () => onDeleteMedia(props.row.id))" data-cy="btn-delete"
|
||||
:aria-label="$t('globals.buttons.delete')">
|
||||
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
|
||||
<b-icon icon="trash-can-outline" size="is-small" />
|
||||
</b-tooltip>
|
||||
</a>
|
||||
</b-table-column>
|
||||
|
||||
<template #empty v-if="!loading.media">
|
||||
<empty-placeholder />
|
||||
</template>
|
||||
</b-table>
|
||||
<!-- Pagination -->
|
||||
<div v-if="media.total > media.perPage" class="pagination-wrapper mt-5">
|
||||
<b-pagination :total="media.total" :current.sync="media.page" :per-page="media.perPage"
|
||||
@change="onPageChange" />
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</template>
|
||||
@@ -121,6 +132,7 @@ export default Vue.extend({
|
||||
},
|
||||
toUpload: 0,
|
||||
uploaded: 0,
|
||||
showUploadForm: false,
|
||||
|
||||
queryParams: {
|
||||
page: 1,
|
||||
@@ -141,6 +153,11 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
onToggleForm() {
|
||||
this.showUploadForm = !this.showUploadForm;
|
||||
this.$utils.setPref('media.upload', this.showUploadForm);
|
||||
},
|
||||
|
||||
onQueryMedia() {
|
||||
this.queryParams.page = 1;
|
||||
this.getMedia();
|
||||
@@ -196,7 +213,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['loading', 'media', 'settings']),
|
||||
...mapState(['loading', 'media', 'serverConfig']),
|
||||
|
||||
isProcessing() {
|
||||
if (this.toUpload > 0 && this.uploaded < this.toUpload) {
|
||||
@@ -208,6 +225,10 @@ export default Vue.extend({
|
||||
|
||||
mounted() {
|
||||
this.$api.getMedia();
|
||||
|
||||
if (this.$utils.getPref('media.upload')) {
|
||||
this.showUploadForm = true;
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -154,9 +154,9 @@ export default Vue.extend({
|
||||
hasDummy = 'sendgrid';
|
||||
}
|
||||
|
||||
if (this.isDummy(form['security.captcha_secret'])) {
|
||||
form['security.captcha_secret'] = '';
|
||||
} else if (this.hasDummy(form['security.captcha_secret'])) {
|
||||
if (this.isDummy(form['security.captcha'].hcaptcha.secret)) {
|
||||
form['security.captcha'].hcaptcha.secret = '';
|
||||
} else if (this.hasDummy(form['security.captcha'].hcaptcha.secret)) {
|
||||
hasDummy = 'captcha';
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,11 @@
|
||||
<pre v-if="visibleMeta[props.row.id]">{{ props.row.meta }}</pre>
|
||||
</b-table-column>
|
||||
</b-table>
|
||||
</b-tab-item>
|
||||
</b-tab-item><!-- bounces -->
|
||||
|
||||
<b-tab-item :label="$t('subscribers.activity')" class="activity" :disabled="!isEditing">
|
||||
<subscriber-activity v-if="isEditing && data.id" :subscriber-id="data.id" />
|
||||
</b-tab-item><!-- activity -->
|
||||
</b-tabs>
|
||||
|
||||
<b-field :message="$t('subscribers.attribsHelp') + ' ' + egAttribs" class="mt-6">
|
||||
@@ -168,11 +172,13 @@ import Vue from 'vue';
|
||||
import { mapState } from 'vuex';
|
||||
import ListSelector from '../components/ListSelector.vue';
|
||||
import CopyText from '../components/CopyText.vue';
|
||||
import SubscriberActivity from '../components/SubscriberActivity.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
ListSelector,
|
||||
CopyText,
|
||||
SubscriberActivity,
|
||||
},
|
||||
|
||||
props: {
|
||||
|
||||
@@ -38,20 +38,114 @@
|
||||
</b-button>
|
||||
</b-field>
|
||||
</form>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<!-- 2FA -->
|
||||
<section v-if="this.data.passwordLogin" class="twofa-section">
|
||||
<!-- TOTP disabled -->
|
||||
<div v-if="data.twofaType === 'none'" class="box">
|
||||
<div class="columns is-vcentered mb-4">
|
||||
<div class="column">
|
||||
<h3 class="title is-size-5 mb-0">{{ $t('users.twoFA') }}</h3>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<b-switch v-if="!isTotpVisible" v-model="twofaEnabled" @input="onToggleEnableTotp" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>{{ $t('users.twoFANotEnabled') }}</p>
|
||||
<br />
|
||||
|
||||
<!-- TOTP setup -->
|
||||
<div v-if="isTotpVisible" class="totp-setup">
|
||||
<div v-if="totpQR" class="qr-section">
|
||||
<p class="has-text-grey">{{ $t('users.totpScanQR') }}</p><br />
|
||||
|
||||
<img :src="'data:image/png;base64,' + totpQR" alt="QR Code" />
|
||||
|
||||
<br /><br />
|
||||
<p>
|
||||
<strong>{{ $t('users.totpSecret') }}</strong><br />
|
||||
<code><copy-text :text="`${totpSecret}`" /></code>
|
||||
</p>
|
||||
|
||||
<br /><br />
|
||||
<form @submit.prevent="confirmTOTP">
|
||||
<b-field :label="$t('users.totpCode')" label-position="on-border">
|
||||
<b-input ref="totpCodeInput" v-model="totpCode" maxlength="6" pattern="[0-9]{6}" placeholder="000000"
|
||||
required />
|
||||
</b-field>
|
||||
<div class="buttons">
|
||||
<b-button type="is-primary" native-type="submit">
|
||||
{{ $t('globals.buttons.enable') }}
|
||||
</b-button>
|
||||
<b-button type="button" @click="onCancelTOTPSetup">
|
||||
{{ $t('globals.buttons.cancel') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TOTP Enabled -->
|
||||
<div v-if="data.twofaType === 'totp'" class="box">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column">
|
||||
<h3 class="title is-size-5">
|
||||
<b-icon icon="check-circle-outline" type="is-success" /> {{ $t('users.twoFAEnabled') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<b-switch v-if="!showDisableTOTP" v-model="twofaEnabled" @input="toggleDisableTOTP" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>{{ $t('users.twoFAEnabledDesc', { type: data.twofaType.toUpperCase() }) }}</p>
|
||||
|
||||
<!-- Disable TOTP Flow -->
|
||||
<form v-if="showDisableTOTP" class="disable-totp mt-5" @submit.prevent="confirmDisableTOTP">
|
||||
<b-field :label="$t('users.password')" label-position="on-border">
|
||||
<b-input ref="disablePasswordInput" v-model="disableTOTPPassword" type="password" minlength="8" required />
|
||||
</b-field>
|
||||
<div class="buttons">
|
||||
<b-button type="is-danger" native-type="submit">
|
||||
{{ $t('globals.buttons.disable') }}
|
||||
</b-button>
|
||||
<b-button type="button" @click="onCancelTOTPSetup">
|
||||
{{ $t('globals.buttons.cancel') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import { mapState } from 'vuex';
|
||||
import CopyText from '../components/CopyText.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'UserProfile',
|
||||
|
||||
components: {
|
||||
CopyText,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
data: {},
|
||||
isTotpVisible: false,
|
||||
totpQR: null,
|
||||
totpSecret: null,
|
||||
totpCode: '',
|
||||
showDisableTOTP: false,
|
||||
disableTOTPPassword: '',
|
||||
twofaEnabled: false,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -78,12 +172,101 @@ export default Vue.extend({
|
||||
this.$utils.toast(this.$t('globals.messages.updated', { name: this.data.username }));
|
||||
});
|
||||
},
|
||||
|
||||
onToggleEnableTotp() {
|
||||
this.$api.getTOTPQR(this.data.id).then((data) => {
|
||||
this.totpQR = data.qr;
|
||||
this.totpSecret = data.secret;
|
||||
this.isTotpVisible = true;
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.totpCodeInput) {
|
||||
this.$refs.totpCodeInput.focus();
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
this.$utils.toast(this.$t('globals.messages.errorFetching'), 'is-danger');
|
||||
});
|
||||
},
|
||||
|
||||
onCancelTOTPSetup() {
|
||||
this.isTotpVisible = false;
|
||||
this.totpQR = null;
|
||||
this.totpSecret = null;
|
||||
this.totpCode = '';
|
||||
this.twofaEnabled = this.data.twofaType === 'totp';
|
||||
this.showDisableTOTP = false;
|
||||
this.disableTOTPPassword = '';
|
||||
},
|
||||
|
||||
confirmTOTP() {
|
||||
if (!this.totpCode || this.totpCode.length !== 6) {
|
||||
this.$utils.toast(this.$t('globals.messages.invalidValue'), 'is-danger');
|
||||
return;
|
||||
}
|
||||
|
||||
const d = new FormData();
|
||||
d.append('secret', this.totpSecret);
|
||||
d.append('code', this.totpCode);
|
||||
|
||||
this.$api.enableTOTP(this.data.id, d).then(() => {
|
||||
this.$utils.toast(this.$t('users.twoFAEnabled'));
|
||||
this.onCancelTOTPSetup();
|
||||
|
||||
// Reload user profile
|
||||
this.$api.getUserProfile().then((data) => {
|
||||
this.data = { ...data };
|
||||
this.twofaEnabled = data.twofaType === 'totp';
|
||||
});
|
||||
}).catch(() => {
|
||||
this.$utils.toast(this.$t('globals.messages.invalidValue'), 'is-danger');
|
||||
});
|
||||
},
|
||||
|
||||
toggleDisableTOTP() {
|
||||
this.showDisableTOTP = true;
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.disablePasswordInput) {
|
||||
this.$refs.disablePasswordInput.focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cancelDisableTOTP() {
|
||||
this.showDisableTOTP = false;
|
||||
this.disableTOTPPassword = '';
|
||||
},
|
||||
|
||||
confirmDisableTOTP() {
|
||||
if (!this.disableTOTPPassword) {
|
||||
this.$utils.toast(this.$t('globals.messages.invalidFields'), 'is-danger');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('password', this.disableTOTPPassword);
|
||||
|
||||
this.$api.disableTOTP(this.data.id, formData).then(() => {
|
||||
this.$utils.toast(this.$t('globals.messages.done'));
|
||||
this.showDisableTOTP = false;
|
||||
this.disableTOTPPassword = '';
|
||||
// Reload user profile
|
||||
this.$api.getUserProfile().then((data) => {
|
||||
this.data = { ...data };
|
||||
this.twofaEnabled = data.twofaType === 'totp';
|
||||
});
|
||||
}).catch(() => {
|
||||
this.$utils.toast(this.$t('users.invalidPassword'), 'is-danger');
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$api.getUserProfile().then((data) => {
|
||||
this.data = { ...data };
|
||||
this.form = { name: data.name, email: data.email };
|
||||
this.twofaEnabled = data.twofaType === 'totp';
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -31,16 +31,59 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-field :label="$t('settings.security.OIDCClientID')" label-position="on-border">
|
||||
<b-input v-model="data['security.oidc']['client_id']" name="oidc.client_id" ref="client_id"
|
||||
:disabled="!data['security.oidc']['enabled']" :maxlength="200" required />
|
||||
</b-field>
|
||||
<div class="columns">
|
||||
<div class="column is-6">
|
||||
<b-field :label="$t('settings.security.OIDCClientID')" label-position="on-border">
|
||||
<b-input v-model="data['security.oidc']['client_id']" name="oidc.client_id" ref="client_id"
|
||||
:disabled="!data['security.oidc']['enabled']" :maxlength="200" required />
|
||||
</b-field>
|
||||
</div>
|
||||
|
||||
<b-field :label="$t('settings.security.OIDCClientSecret')" label-position="on-border">
|
||||
<b-input v-model="data['security.oidc']['client_secret']" name="oidc.client_secret" type="password"
|
||||
:disabled="!data['security.oidc']['enabled']" :maxlength="200" required />
|
||||
</b-field>
|
||||
<div class="column is-6">
|
||||
<b-field :label="$t('settings.security.OIDCClientSecret')" label-position="on-border">
|
||||
<b-input v-model="data['security.oidc']['client_secret']" name="oidc.client_secret" type="password"
|
||||
:disabled="!data['security.oidc']['enabled']" :maxlength="200" required />
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<b-field :label="$t('settings.security.OIDCAutoCreateUsers')"
|
||||
:message="$t('settings.security.OIDCAutoCreateUsersHelp')">
|
||||
<b-switch v-model="data['security.oidc']['auto_create_users']"
|
||||
:disabled="!data['security.oidc']['enabled']" name="oidc.auto_create_users" />
|
||||
</b-field>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<b-field :label="$t('settings.security.OIDCDefaultUserRole')" label-position="on-border"
|
||||
:message="$t('settings.security.OIDCDefaultRoleHelp')">
|
||||
<b-select v-model="data['security.oidc']['default_user_role_id']"
|
||||
:disabled="!data['security.oidc']['enabled'] || !data['security.oidc']['auto_create_users']"
|
||||
name="oidc.default_user_role_id" expanded>
|
||||
<option v-for="role in userRoles" :key="role.id" :value="role.id">
|
||||
{{ role.name }}
|
||||
</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<b-field :label="$t('settings.security.OIDCDefaultListRole')" label-position="on-border"
|
||||
:message="$t('settings.security.OIDCDefaultRoleHelp')">
|
||||
<b-select v-model="data['security.oidc']['default_list_role_id']"
|
||||
:disabled="!data['security.oidc']['enabled'] || !data['security.oidc']['auto_create_users']"
|
||||
name="oidc.default_list_role_id" expanded>
|
||||
<option :value="null">— {{ $t("globals.terms.none") }} —</option>
|
||||
<option v-for="role in listRoles" :key="role.id" :value="role.id">
|
||||
{{ role.name }}
|
||||
</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<b-field :label="$t('settings.security.OIDCRedirectURL')">
|
||||
<code><copy-text :text="`${serverConfig.root_url}/auth/oidc`" /></code>
|
||||
</b-field>
|
||||
@@ -55,21 +98,54 @@
|
||||
<div class="columns">
|
||||
<div class="column is-3">
|
||||
<b-field :label="$t('settings.security.enableCaptcha')" :message="$t('settings.security.enableCaptchaHelp')">
|
||||
<b-switch v-model="data['security.enable_captcha']" name="security.captcha" />
|
||||
<b-switch v-model="captchaEnabled" name="security.captcha" />
|
||||
</b-field>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<b-field :label="$t('settings.security.captchaKey')" label-position="on-border"
|
||||
:message="$t('settings.security.captchaKeyHelp')">
|
||||
<b-input v-model="data['security.captcha_key']" name="captcha_key"
|
||||
:disabled="!data['security.enable_captcha']" :maxlength="200" required />
|
||||
<div class="column is-9" v-if="captchaEnabled">
|
||||
<b-field :label="$t('settings.security.captchaProvider')">
|
||||
<b-radio v-model="selectedProvider" native-value="altcha" name="captcha_provider">
|
||||
ALTCHA
|
||||
</b-radio>
|
||||
<b-radio v-model="selectedProvider" native-value="hcaptcha" name="captcha_provider">
|
||||
hCaptcha (deprecated)
|
||||
</b-radio>
|
||||
</b-field>
|
||||
<b-field :label="$t('settings.security.captchaSecret')" label-position="on-border">
|
||||
<b-input v-model="data['security.captcha_secret']" name="captcha_secret" type="password"
|
||||
:disabled="!data['security.enable_captcha']" :maxlength="200" required />
|
||||
|
||||
<!-- captcha settings -->
|
||||
<div v-if="selectedProvider === 'altcha'">
|
||||
<b-field :label="$t('settings.security.altchaComplexity')" label-position="on-border"
|
||||
:message="$t('settings.security.altchaComplexityHelp')">
|
||||
<b-input v-model.number="data['security.captcha']['altcha']['complexity']" name="altcha_complexity"
|
||||
type="number" min="1000" max="1000000" required />
|
||||
</b-field>
|
||||
</div>
|
||||
<div v-if="selectedProvider === 'hcaptcha'">
|
||||
<b-field :label="$t('settings.security.captchaKey')" label-position="on-border"
|
||||
:message="$t('settings.security.captchaKeyHelp')">
|
||||
<b-input v-model="data['security.captcha']['hcaptcha']['key']" name="hcaptcha_key" :maxlength="200"
|
||||
required />
|
||||
</b-field>
|
||||
<b-field :label="$t('settings.security.captchaSecret')" label-position="on-border">
|
||||
<b-input v-model="data['security.captcha']['hcaptcha']['secret']" name="hcaptcha_secret" type="password"
|
||||
:maxlength="200" required />
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- captcha -->
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- CORS -->
|
||||
<div class="columns">
|
||||
<div class="column is-12">
|
||||
<h3 class="is-size-6"><strong>CORS</strong></h3><br />
|
||||
<b-field :label="$t('settings.security.CORSDomains')" label-position="on-border"
|
||||
:message="$t('settings.security.CORSDomainsHelp')">
|
||||
<b-input v-model="corsDomains" name="cors_origins" type="textarea" rows="5"
|
||||
placeholder="https://example.com" />
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- cors -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -97,7 +173,42 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['serverConfig']),
|
||||
...mapState(['serverConfig', 'userRoles', 'listRoles']),
|
||||
|
||||
corsDomains: {
|
||||
get() {
|
||||
// Convert array to newline-separated string.
|
||||
const domains = this.data['security.cors_origins'];
|
||||
return domains && Array.isArray(domains) ? domains.join('\n') : '';
|
||||
},
|
||||
set(value) {
|
||||
this.$set(this.data, 'security.cors_origins', value.split('\n'));
|
||||
},
|
||||
},
|
||||
|
||||
captchaEnabled: {
|
||||
get() {
|
||||
return this.data['security.captcha'].altcha.enabled || this.data['security.captcha'].hcaptcha.enabled;
|
||||
},
|
||||
set(value) {
|
||||
this.data['security.captcha'].altcha.enabled = !!value;
|
||||
this.data['security.captcha'].hcaptcha.enabled = false;
|
||||
},
|
||||
},
|
||||
|
||||
selectedProvider: {
|
||||
get() {
|
||||
if (this.data['security.captcha'].hcaptcha.enabled) {
|
||||
return 'hcaptcha';
|
||||
}
|
||||
|
||||
return 'altcha';
|
||||
},
|
||||
set(value) {
|
||||
this.data['security.captcha'].hcaptcha.enabled = value === 'hcaptcha';
|
||||
this.data['security.captcha'].altcha.enabled = value === 'altcha';
|
||||
},
|
||||
},
|
||||
|
||||
version() {
|
||||
return import.meta.env.VUE_APP_VERSION;
|
||||
@@ -117,6 +228,11 @@ export default Vue.extend({
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$api.getUserRoles();
|
||||
this.$api.getListRoles();
|
||||
},
|
||||
|
||||
methods: {
|
||||
setProvider(provider) {
|
||||
this.$set(this.data['security.oidc'], 'provider_url', OIDC_PROVIDERS[provider]);
|
||||
|
||||
59
frontend/yarn.lock
vendored
@@ -2,6 +2,11 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@altcha/crypto@^0.0.1":
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@altcha/crypto/-/crypto-0.0.1.tgz#0e2f254559fb350c80ff56d29b8e3ab2e6bbea95"
|
||||
integrity sha512-qZMdnoD3lAyvfSUMNtC2adRi666Pxdcw9zqfMU5qBOaJWqpN9K+eqQGWqeiKDMqL0SF+EytNG4kR/Pr/99GJ6g==
|
||||
|
||||
"@babel/helper-string-parser@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
|
||||
@@ -904,6 +909,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz#c2dcd8a4b08b2f2778eceb7a5a5dfde6240ebdea"
|
||||
integrity sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942"
|
||||
integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.30.1":
|
||||
version "4.30.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz#183637d91456877cb83d0a0315eb4788573aa588"
|
||||
@@ -1046,6 +1056,15 @@ ajv@^6.12.4:
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
altcha@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/altcha/-/altcha-2.1.0.tgz#8502afbdb34fa799474f203939d6de1cb622bc2b"
|
||||
integrity sha512-V+Ug4Qr9oLR2WcDSgtzmwOzHGqoencaJoyRmUb1hqZ6Y91B3Xe8vHQ0Q3RwoKuBOYHX9yTYccJvaWHEp8+4UIQ==
|
||||
dependencies:
|
||||
"@altcha/crypto" "^0.0.1"
|
||||
optionalDependencies:
|
||||
"@rollup/rollup-linux-x64-gnu" "4.18.0"
|
||||
|
||||
ansi-colors@^4.1.1:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
|
||||
@@ -1232,13 +1251,13 @@ axe-core@^4.10.0:
|
||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df"
|
||||
integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==
|
||||
|
||||
axios@^1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979"
|
||||
integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==
|
||||
axios@^1.12.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.0.tgz#11248459be05a5ee493485628fa0e4323d0abfc3"
|
||||
integrity sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.6"
|
||||
form-data "^4.0.0"
|
||||
form-data "^4.0.4"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
axobject-query@^4.1.0:
|
||||
@@ -2293,13 +2312,15 @@ forever-agent@~0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==
|
||||
|
||||
form-data@^4.0.0, form-data@~4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48"
|
||||
integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==
|
||||
form-data@^4.0.4, form-data@~4.0.0:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
|
||||
integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
hasown "^2.0.2"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fs-extra@^9.1.0:
|
||||
@@ -2872,9 +2893,9 @@ js-cookie@^3.0.5:
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
|
||||
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
@@ -3925,9 +3946,9 @@ tldts@^6.1.32:
|
||||
tldts-core "^6.1.71"
|
||||
|
||||
tmp@~0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae"
|
||||
integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.4.tgz#c6db987a2ccc97f812f17137b36af2b6521b0d13"
|
||||
integrity sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
@@ -4090,10 +4111,10 @@ verror@1.10.0:
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
vite@^5.4.19:
|
||||
version "5.4.19"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.19.tgz#20efd060410044b3ed555049418a5e7d1998f959"
|
||||
integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==
|
||||
vite@^5.4.21:
|
||||
version "5.4.21"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027"
|
||||
integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==
|
||||
dependencies:
|
||||
esbuild "^0.21.3"
|
||||
postcss "^8.4.43"
|
||||
|
||||
43
go.mod
@@ -4,6 +4,7 @@ go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/altcha-org/altcha-lib-go v0.2.2
|
||||
github.com/coreos/go-oidc/v3 v3.14.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/emersion/go-message v0.18.2
|
||||
@@ -19,34 +20,35 @@ require (
|
||||
github.com/knadh/koanf/providers/confmap v1.0.0
|
||||
github.com/knadh/koanf/providers/env v1.1.0
|
||||
github.com/knadh/koanf/providers/file v1.2.0
|
||||
github.com/knadh/koanf/providers/posflag v1.0.0
|
||||
github.com/knadh/koanf/providers/posflag v1.0.1
|
||||
github.com/knadh/koanf/providers/rawbytes v1.0.0
|
||||
github.com/knadh/koanf/v2 v2.2.0
|
||||
github.com/knadh/koanf/v2 v2.2.2
|
||||
github.com/knadh/paginator v1.0.1
|
||||
github.com/knadh/smtppool/v2 v2.0.0
|
||||
github.com/knadh/smtppool/v2 v2.0.1
|
||||
github.com/knadh/stuffbin v1.3.0
|
||||
github.com/labstack/echo/v4 v4.13.3
|
||||
github.com/labstack/echo/v4 v4.13.4
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/paulbellamy/ratecounter v0.2.0
|
||||
github.com/rhnvrm/simples3 v0.9.0
|
||||
github.com/rhnvrm/simples3 v0.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/yuin/goldmark v1.7.10
|
||||
github.com/yuin/goldmark v1.7.12
|
||||
github.com/zerodha/easyjson v1.0.1
|
||||
github.com/zerodha/simplesessions/stores/postgres/v3 v3.0.0
|
||||
github.com/zerodha/simplesessions/v3 v3.0.0
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/oauth2 v0.29.0
|
||||
golang.org/x/text v0.24.0
|
||||
golang.org/x/mod v0.29.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/text v0.31.0
|
||||
gopkg.in/volatiletech/null.v6 v6.0.0-20170828023728-0bef4e07ae1b
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@@ -56,15 +58,14 @@ require (
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pquerna/otp v1.5.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/cast v1.9.2 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/image v0.26.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/image v0.29.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.8
|
||||
|
||||
87
go.sum
@@ -1,13 +1,17 @@
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/altcha-org/altcha-lib-go v0.2.2 h1:KY7a7jFUf6tFKZF6MzuZMhSWuGMv0MtVkK/Kj4Oas38=
|
||||
github.com/altcha-org/altcha-lib-go v0.2.2/go.mod h1:I8ESLVWR9C58uvGufB/AJDPhaSU4+4Oh3DLpVtgwDAk=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -25,18 +29,18 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gdgvda/cron v0.4.0 h1:hGXFhEXisTqOByNVf2YEL3pWcND90Acjf4gjF4S+ICg=
|
||||
github.com/gdgvda/cron v0.4.0/go.mod h1:caBF+mzTZGtQqFE05T1m6u9OmCASY3EK51XAICf3wio=
|
||||
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
|
||||
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
|
||||
github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
|
||||
@@ -64,24 +68,24 @@ github.com/knadh/koanf/providers/env v1.1.0 h1:U2VXPY0f+CsNDkvdsG8GcsnK4ah85WwWy
|
||||
github.com/knadh/koanf/providers/env v1.1.0/go.mod h1:QhHHHZ87h9JxJAn2czdEl6pdkNnDh/JS1Vtsyt65hTY=
|
||||
github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U=
|
||||
github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
|
||||
github.com/knadh/koanf/providers/posflag v1.0.0 h1:1hroGpfVOKZA+1uFiPXHlAPyBXsv8U8xt9oInMTWYcM=
|
||||
github.com/knadh/koanf/providers/posflag v1.0.0/go.mod h1:3Wn3+YG3f4ljzRyCUgIwH7G0sZ1pMjCOsNBovrbKmAk=
|
||||
github.com/knadh/koanf/providers/posflag v1.0.1 h1:EnMxHSrPkYCFnKgBUl5KBgrjed8gVFrcXDzaW4l/C6Y=
|
||||
github.com/knadh/koanf/providers/posflag v1.0.1/go.mod h1:3Wn3+YG3f4ljzRyCUgIwH7G0sZ1pMjCOsNBovrbKmAk=
|
||||
github.com/knadh/koanf/providers/rawbytes v1.0.0 h1:MrKDh/HksJlKJmaZjgs4r8aVBb/zsJyc/8qaSnzcdNI=
|
||||
github.com/knadh/koanf/providers/rawbytes v1.0.0/go.mod h1:KxwYJf1uezTKy6PBtfE+m725NGp4GPVA7XoNTJ/PtLo=
|
||||
github.com/knadh/koanf/v2 v2.2.0 h1:FZFwd9bUjpb8DyCWARUBy5ovuhDs1lI87dOEn2K8UVU=
|
||||
github.com/knadh/koanf/v2 v2.2.0/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY=
|
||||
github.com/knadh/koanf/v2 v2.2.2 h1:ghbduIkpFui3L587wavneC9e3WIliCgiCgdxYO/wd7A=
|
||||
github.com/knadh/koanf/v2 v2.2.2/go.mod h1:abWQc0cBXLSF/PSOMCB/SK+T13NXDsPvOksbpi5e/9Q=
|
||||
github.com/knadh/paginator v1.0.1 h1:mzbhjx167qRpvv9ym7BBaaNa1//qXU2YKk7mTmSmW9o=
|
||||
github.com/knadh/paginator v1.0.1/go.mod h1:NDzJofYUQiHbDrWukKRi3E2vIKb0dzOZKY8VmxbM2Sk=
|
||||
github.com/knadh/smtppool/v2 v2.0.0 h1:Xy18flerfkV7t3iai4m3DO4BMdYTme1WRhzNclAEeYU=
|
||||
github.com/knadh/smtppool/v2 v2.0.0/go.mod h1:D7HcfSS8Xd3jpZ9LRwQ3aGdqp9FzFE66uW6w/BTpy4E=
|
||||
github.com/knadh/smtppool/v2 v2.0.1 h1:nHlJvkVynjUZFuFVz1pkry6mHbCHraUJkg/+swzuBfo=
|
||||
github.com/knadh/smtppool/v2 v2.0.1/go.mod h1:D7HcfSS8Xd3jpZ9LRwQ3aGdqp9FzFE66uW6w/BTpy4E=
|
||||
github.com/knadh/stuffbin v1.3.0 h1:HaVSuYV+KnrlCHl7DrLNyOCgpTU2K8x5Hb+J4Ck3gww=
|
||||
github.com/knadh/stuffbin v1.3.0/go.mod h1:yVCFaWaKPubSNibBsTAJ939q2ABHudJQxRWZWV5yh+4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
||||
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@@ -104,19 +108,22 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rhnvrm/simples3 v0.9.0 h1:It6/glyqRTRooRzXcYOuqpKwjGg3lsXgNmeGgxpBtjA=
|
||||
github.com/rhnvrm/simples3 v0.9.0/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/rhnvrm/simples3 v0.9.1 h1:pYfEe2wTjx8B2zFzUdy4kZn3I3Otd9ZvzIhHkFR85kE=
|
||||
github.com/rhnvrm/simples3 v0.9.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
@@ -127,8 +134,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
|
||||
github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
|
||||
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/zerodha/easyjson v1.0.1 h1:GTdVnhd1RxUSeTGua6YTy2ZC7ivywWBeZ9NoyoFaQdM=
|
||||
github.com/zerodha/easyjson v1.0.1/go.mod h1:mA8d8Xs8Yp4Q95ppRb4dRGROERgKSLQIK9Y7iuC5mog=
|
||||
github.com/zerodha/simplesessions/stores/postgres/v3 v3.0.0 h1:50BNRW/VYOgCf5v6vbhKMT40sFA+yZ7xUrdM/vbI1G8=
|
||||
@@ -137,24 +144,24 @@ github.com/zerodha/simplesessions/v3 v3.0.0 h1:seHwxVNnlCbp5nG8GFxSsRUdiHnfb39Qd
|
||||
github.com/zerodha/simplesessions/v3 v3.0.0/go.mod h1:lAK+CJmZRlbvfq+OnkB8Iyf6LWgjzvUuWYKX1XA51P0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
||||
golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas=
|
||||
golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -165,8 +172,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -177,10 +184,10 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
||||
34
i18n/bg.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Без списък",
|
||||
"email.data.info": "Копие на всички данни, записани за вас, е прикачено като файл в JSON формат. Може да се прегледа в текстов редактор.",
|
||||
"email.data.title": "Вашите данни",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Потвърждаване на абонамент",
|
||||
"email.optin.confirmSubHelp": "Потвърдете абонамента си, като щракнете върху бутона по-долу.",
|
||||
"email.optin.confirmSubInfo": "Бяхте добавени към следните списъци:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Копиране",
|
||||
"globals.buttons.delete": "Изтриване",
|
||||
"globals.buttons.deleteAll": "Изтриване на всички",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Редактиране",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Активирано",
|
||||
"globals.buttons.insert": "Вмъкване",
|
||||
"globals.buttons.learnMore": "Научете повече",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Невалидни полета: {name}",
|
||||
"globals.messages.invalidID": "Невалидно ID(и)",
|
||||
"globals.messages.invalidUUID": "Невалиден UUID(и)",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Липсващи полета: {name}",
|
||||
"globals.messages.notFound": "{name} не е намерен",
|
||||
"globals.messages.numSelected": "{num} избрани",
|
||||
"globals.messages.passwordChange": "Въведете стойност за промяна",
|
||||
"globals.messages.passwordChangeFull": "Изчистете и въведете отново пълната парола в '{name}'.",
|
||||
"globals.messages.permissionDenied": "Достъпът е отказан: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Шаблон | Шаблони",
|
||||
"globals.terms.templates": "Шаблони",
|
||||
"globals.terms.tx": "Транзакционен | Транзакционни",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Потребител | Потребители",
|
||||
"globals.terms.users": "Потребители",
|
||||
"globals.terms.year": "Година | Години",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Неподдържан тип файл ({type})",
|
||||
"media.upload": "Качване",
|
||||
"media.uploadHelp": "Щракнете или плъзнете едно или повече изображения тук",
|
||||
"media.uploadImage": "Качване на изображение",
|
||||
"menu.allCampaigns": "Всички кампании",
|
||||
"menu.allLists": "Всички списъци",
|
||||
"menu.allSubscribers": "Всички абонати",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Записване на IP адреса на opt-in",
|
||||
"settings.privacy.recordOptinIPHelp": "Записване на IP адреса на двойния opt-in в атрибутите на абоната.",
|
||||
"settings.restart": "Рестартиране",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Автоматично създаване на потребители",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Автоматично създаване на потребител при първо влизане, ако акаунтът не съществува.",
|
||||
"settings.security.OIDCClientID": "ID на клиент",
|
||||
"settings.security.OIDCClientSecret": "Клиентска тайна",
|
||||
"settings.security.OIDCDefaultListRole": "По подразбиране роля в списъка",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Роля по подразбиране, задавана на потребители, създадени автоматично чрез OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "По подразбиране роля на потребителя",
|
||||
"settings.security.OIDCHelp": "Активиране на OpenID Connect OAuth2 вход чрез OAuth доставчик.",
|
||||
"settings.security.OIDCName": "Име на доставчика",
|
||||
"settings.security.OIDCRedirectURL": "URL за пренасочване за oAuth доставчик",
|
||||
"settings.security.OIDCRedirectWarning": "Това не изглежда като производствен URL. Променете основния URL в настройките 'Общи'.",
|
||||
"settings.security.OIDCURL": "URL на доставчика",
|
||||
"settings.security.OIDCWarning": "Когато OIDC е активиран, входът с парола по подразбиране е деактивиран. Невалидната конфигурация може да ви заключи.",
|
||||
"settings.security.altchaComplexity": "Altcha сложност",
|
||||
"settings.security.altchaComplexityHelp": "По-високи стойности осигуряват по-добра сигурност, но по-бавно решаване (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com SiteKey",
|
||||
"settings.security.captchaKeyHelp": "Посетете www.hcaptcha.com, за да получите ключа и тайната.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com тайна",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "До имейл",
|
||||
"settings.title": "Настройки",
|
||||
"settings.updateAvailable": "Налична е нова актуализация {version}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Разширено",
|
||||
"subscribers.advancedQueryHelp": "Частичен SQL израз за заявка за атрибути на абонати",
|
||||
"subscribers.attribs": "Атрибути",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Копирайте API токена за достъп сега. Той няма да бъде показан отново.",
|
||||
"users.cantDeleteRole": "Не може да се изтрие роля, която се използва.",
|
||||
"users.firstTime": "Това е нова инсталация. Изберете потребителско име и парола за акаунта на Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Невалидно потребителско име или парола",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Невалидна заявка за удостоверяване",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Последно влизане",
|
||||
"users.listPerms": "Разрешения за списъци",
|
||||
"users.listPermsWarning": "lists:get_all или lists:manage_all са активирани, което отменя разрешенията за отделните списъци",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Изход",
|
||||
"users.needSuper": "Потребител(и) не можеха да бъдат актуализирани. Трябва да има поне един активен потребител Super Admin.",
|
||||
"users.newListRole": "Нова роля на списък",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Нов потребител",
|
||||
"users.newUserRole": "Нова потребителска роля",
|
||||
"users.password": "Парола",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Повторете паролата",
|
||||
"users.perms": "Разрешения",
|
||||
"users.profile": "Профил",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Роля | Роли",
|
||||
"users.roleGroup": "Група",
|
||||
"users.roles": "Роли",
|
||||
"users.status.disabled": "Деактивиран",
|
||||
"users.status.enabled": "Активиран",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Тип",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Супер Админ",
|
||||
|
||||
34
i18n/ca.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Orfes",
|
||||
"email.data.info": "S'adjunta una còpia de totes les dades enregistrades sobre la teva persona en un fitxer en format JSON. Es pot veure en un editor de text.",
|
||||
"email.data.title": "Les teves dades ",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirma la subscripció",
|
||||
"email.optin.confirmSubHelp": "Confirmeu la terva subscripció fent clic al botó següent.",
|
||||
"email.optin.confirmSubInfo": "Heu estat afegit a les llistes següents:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copiar",
|
||||
"globals.buttons.delete": "Esborra",
|
||||
"globals.buttons.deleteAll": "Esborra tot",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Edita",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Habilitat",
|
||||
"globals.buttons.insert": "Inserta",
|
||||
"globals.buttons.learnMore": "Saber-nes més",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Camps no vàlids: {name}",
|
||||
"globals.messages.invalidID": "ID(s) no vàlid",
|
||||
"globals.messages.invalidUUID": "UUID(s) no vàlid",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Falten camps: {name}",
|
||||
"globals.messages.notFound": "No s'ha trobat {name} ",
|
||||
"globals.messages.numSelected": "{num} seleccionats",
|
||||
"globals.messages.passwordChange": "Introduïu un valor per canviar",
|
||||
"globals.messages.passwordChangeFull": "Buida i torna a introduir la contrasenya completa a '{name}'.",
|
||||
"globals.messages.permissionDenied": "Permís denegat: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Plantilla | Plantilles",
|
||||
"globals.terms.templates": "Plantilles",
|
||||
"globals.terms.tx": "Transaccional | Transaccionals",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Usuari | Usuaris",
|
||||
"globals.terms.users": "Usuaris",
|
||||
"globals.terms.year": "Any | Anys",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "El tipus de fitxer ({type}) no és compatible",
|
||||
"media.upload": "Carrega",
|
||||
"media.uploadHelp": "Fes clic o arrossega una o més imatges aquí",
|
||||
"media.uploadImage": "Carrega la imatge",
|
||||
"menu.allCampaigns": "Totes les campanyes",
|
||||
"menu.allLists": "Totes les llistes",
|
||||
"menu.allSubscribers": "Tots els subscriptors",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Registra l'adreça IP de l'opt-in",
|
||||
"settings.privacy.recordOptinIPHelp": "Registra l'adreça IP dels opt-ins dobles en els atributs del subscrit.",
|
||||
"settings.restart": "Reinicia",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Crea usuaris automàticament",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Crea automàticament un usuari en el primer inici de sessió si el compte no existeix.",
|
||||
"settings.security.OIDCClientID": "ID del client",
|
||||
"settings.security.OIDCClientSecret": "Secret del client",
|
||||
"settings.security.OIDCDefaultListRole": "Rol de llista per defecte",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Rol per defecte assignat als usuaris creats automàticament des d'OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Rol d'usuari per defecte",
|
||||
"settings.security.OIDCHelp": "Activa l'inici de sessió OAuth2 OpenID Connect a través d'un proveïdor OAuth.",
|
||||
"settings.security.OIDCName": "Nom del proveïdor",
|
||||
"settings.security.OIDCRedirectURL": "URL de redirecció per al proveïdor OAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Això sembla no ser una URL de producció. Canvia la URL principal a la configuració 'General'.",
|
||||
"settings.security.OIDCURL": "URL del proveïdor",
|
||||
"settings.security.OIDCWarning": "Quan s'activa OIDC, l'inici de sessió de contrasenya per defecte es desactiva. Una configuració incorrecta pot bloquejar-te l'accés.",
|
||||
"settings.security.altchaComplexity": "Complexitat Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Valors més alts proporcionen millor seguretat però resolució més lenta (1000-1000000).",
|
||||
"settings.security.captchaKey": "Clau del lloc hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Visiteu www.hcaptcha.com per obtenir la clau i el secret.",
|
||||
"settings.security.captchaSecret": "Secret del lloc hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Destinatari del correu electrònic",
|
||||
"settings.title": "Configuració",
|
||||
"settings.updateAvailable": "Hi ha disponible una nova actualització {versió}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avançat",
|
||||
"subscribers.advancedQueryHelp": "Expressió SQL parcial per consultar els atributs del subscriptor",
|
||||
"subscribers.attribs": "Atributs",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copia l'API access token ara. No es mostrarà de nou.",
|
||||
"users.cantDeleteRole": "No es pot eliminar el rol que s'està utilitzant.",
|
||||
"users.firstTime": "Aquesta és una nova instal·lació. Trieu un nom d'usuari i una contrasenya per al compte d'Administrador Super.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Inici de sessió o contrasenya no vàlids",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Sol·licitud d'autenticació no vàlida",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Últim inici de sessió",
|
||||
"users.listPerms": "Permisos de llista",
|
||||
"users.listPermsWarning": "Estan habilitades les opcions lists:get_all o lists:manage_all, les quals substitueixen els permisos per llista",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Tanca sessió",
|
||||
"users.needSuper": "L'usuari(s) no s'ha actualitzat. S'ha de tenir com a mínim un usuari Super Administrador actiu.",
|
||||
"users.newListRole": "Nou rol de llista",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nou usuari",
|
||||
"users.newUserRole": "Nou rol d'usuari",
|
||||
"users.password": "Contrasenya",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Repeteix la contrasenya",
|
||||
"users.perms": "Permisos",
|
||||
"users.profile": "Perfil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rol | Rols",
|
||||
"users.roleGroup": "Grup",
|
||||
"users.roles": "Rols",
|
||||
"users.status.disabled": "Deshabilitat",
|
||||
"users.status.enabled": "Habilitat",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tipus",
|
||||
"users.type.api": "API d'usuari",
|
||||
"users.type.super": "Super Administrador",
|
||||
|
||||
204
i18n/cs-cz.json
@@ -1,42 +1,42 @@
|
||||
{
|
||||
"_.code": "cs-cz",
|
||||
"_.name": "čeština (cs)",
|
||||
"admin.errorMarshallingConfig": "Chyba konfigurace zařazení: {error}",
|
||||
"_.name": "Čeština (cs)",
|
||||
"admin.errorMarshallingConfig": "Chyba při serializaci konfigurace: {error}",
|
||||
"analytics.count": "Počet",
|
||||
"analytics.fromDate": "Od",
|
||||
"analytics.invalidDates": "Neplatné datum`od` nebo `do`.",
|
||||
"analytics.isUnique": "Počet je počítán na odběratele.",
|
||||
"analytics.invalidDates": "Neplatné datum `od` nebo `do`.",
|
||||
"analytics.isUnique": "Každý odběratel je započítán nejvýše jednou.",
|
||||
"analytics.links": "Odkazy",
|
||||
"analytics.nonUnique": "Protože je sledování odběratelů vypnuté, neexistuje počet na odběratele.",
|
||||
"analytics.nonUnique": "Počty nejsou jedinečné, sledování na úrovni odběratelů je vypnuté.",
|
||||
"analytics.title": "Analytika",
|
||||
"analytics.toDate": "Do",
|
||||
"bounces.complaint": "Stížnost",
|
||||
"bounces.hard": "Tvrdý",
|
||||
"bounces.soft": "Měkký",
|
||||
"bounces.hard": "Trvalý",
|
||||
"bounces.soft": "Dočasný",
|
||||
"bounces.source": "Zdroj",
|
||||
"bounces.unknownService": "Neznámá služba.",
|
||||
"bounces.view": "Zobrazit převzetí",
|
||||
"bounces.view": "Zobrazit nedoručitelnosti",
|
||||
"campaigns.addAltText": "Přidat alternativní zprávu ve formátu prostého textu",
|
||||
"campaigns.addAttachments": "Přidat přílohy",
|
||||
"campaigns.archive": "Archiv",
|
||||
"campaigns.archiveEnable": "Zveřejnit ve veřejném archivu",
|
||||
"campaigns.archiveHelp": "Zveřejnit (bežící, pozastavenou, dokončenou) zprávu kampaně ve veřejném archivu",
|
||||
"campaigns.archiveHelp": "Zveřejnit (běžící, pozastavenou, dokončenou) zprávu kampaně ve veřejném archivu.",
|
||||
"campaigns.archiveMeta": "Metadata kampaně",
|
||||
"campaigns.archiveMetaHelp": "Použít prázdná data přihlášených ve veřejné zpráve včetně jména, emailu a jiných volitelných atributů použitých ve zprávách kampaně nebo šablonách.",
|
||||
"campaigns.archiveSlug": "URL Slug",
|
||||
"campaigns.archiveMetaHelp": "Použít ukázková (dummy) data odběratele ve veřejné zprávě, včetně jména, e-mailu a volitelných atributů použitých v textu kampaně nebo šabloně.",
|
||||
"campaigns.archiveSlug": "URL identifikátor",
|
||||
"campaigns.archiveSlugHelp": "Krátký název stránky používaný v URL. Například: moje-novinky-edice-2",
|
||||
"campaigns.attachments": "Přílohy",
|
||||
"campaigns.cantUpdate": "Nelze aktualizovat spuštěnou nebo dokončenou kampaň.",
|
||||
"campaigns.clicks": "Klepnutí",
|
||||
"campaigns.clicks": "Kliknutí",
|
||||
"campaigns.confirmDelete": "Odstranit {name}",
|
||||
"campaigns.confirmOverwriteContent": "Tím se přepíše veškerý obsah. Pokračovat?",
|
||||
"campaigns.confirmOverwriteContent": "Tato akce přepíše veškerý obsah. Pokračovat?",
|
||||
"campaigns.confirmSchedule": "Tato kampaň se spustí automaticky v naplánované datum a čas. Naplánovat nyní?",
|
||||
"campaigns.confirmSwitchFormat": "Obsah může ztratit formátování. Pokračovat?",
|
||||
"campaigns.content": "Obsah",
|
||||
"campaigns.contentHelp": "Obsah zde",
|
||||
"campaigns.continue": "Pokračovat",
|
||||
"campaigns.copyOf": "Kopie {name}",
|
||||
"campaigns.customHeadersHelp": "Pole volitelných hlaviček k odchozím zprávám, jako: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"campaigns.customHeadersHelp": "Pole volitelných hlaviček k odchozím zprávám, například: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"campaigns.dateAndTime": "Datum a čas",
|
||||
"campaigns.ended": "Ukončeno",
|
||||
"campaigns.errorSendTest": "Chyba při odesílání testu: {error}",
|
||||
@@ -54,7 +54,7 @@
|
||||
"campaigns.importVisualTemplate": "Importovat vizuální šablonu",
|
||||
"campaigns.invalid": "Neplatná kampaň",
|
||||
"campaigns.invalidCustomHeaders": "Neplatné volitelné hlavičky: {error}",
|
||||
"campaigns.markdown": "Sleva",
|
||||
"campaigns.markdown": "Markdown",
|
||||
"campaigns.needsSendAt": "Kampaň musí mít naplánované datum.",
|
||||
"campaigns.newCampaign": "Nová kampaň",
|
||||
"campaigns.noKnownSubsToTest": "Nejsou žádní známí odběratelé k testování.",
|
||||
@@ -73,7 +73,7 @@
|
||||
"campaigns.progress": "Průběh",
|
||||
"campaigns.queryPlaceholder": "Jméno nebo předmět",
|
||||
"campaigns.rateMinuteShort": "min",
|
||||
"campaigns.rawHTML": "Prvotní HTML",
|
||||
"campaigns.rawHTML": "Kód HTML",
|
||||
"campaigns.removeAltText": "Odebrat alternativní zprávu ve formátu prostého textu",
|
||||
"campaigns.richText": "Formátovaný text",
|
||||
"campaigns.schedule": "Naplánovat kampaň",
|
||||
@@ -81,7 +81,7 @@
|
||||
"campaigns.send": "Odeslat",
|
||||
"campaigns.sendLater": "Odeslat později",
|
||||
"campaigns.sendTest": "Odeslat testovací zprávu",
|
||||
"campaigns.sendTestHelp": "Po zapsání adresy stiskněte klávesu Enter, aby se přidalo více příjemců. Adresy musí náležet k existujícím odběratelům.",
|
||||
"campaigns.sendTestHelp": "Po zadání adresy stiskněte Enter pro přidání více příjemců. Adresy musí patřit existujícím odběratelům.",
|
||||
"campaigns.sendToLists": "Seznamy k odeslání",
|
||||
"campaigns.sent": "Odesláno",
|
||||
"campaigns.start": "Spustit kampaň",
|
||||
@@ -102,21 +102,24 @@
|
||||
"campaigns.timestamps": "Časová razítka",
|
||||
"campaigns.trackLink": "Sledovací odkaz",
|
||||
"campaigns.unSchedule": "Zrušit naplánování",
|
||||
"campaigns.views": "Pohledy",
|
||||
"campaigns.views": "Zobrazení",
|
||||
"campaigns.visual": "Vizuální",
|
||||
"dashboard.campaignViews": "Pohledy na kampaň",
|
||||
"dashboard.linkClicks": "Klepnutí na odkaz",
|
||||
"dashboard.campaignViews": "Zobrazení kampaně",
|
||||
"dashboard.linkClicks": "Kliknutí na odkaz",
|
||||
"dashboard.messagesSent": "Zprávy odeslány",
|
||||
"dashboard.orphanSubs": "Samostatní",
|
||||
"email.data.info": "Kopie všech dat, která jste zaznamenali, je připojená jako soubor ve formátu JSON. Lze ji zobrazit v textovém editoru.",
|
||||
"dashboard.orphanSubs": "Sirotci",
|
||||
"email.data.info": "Kopie všech dat, která jsou o vás zaznamenána, je přiložena jako soubor ve formátu JSON. Soubor lze otevřít v libovolném textovém editoru.",
|
||||
"email.data.title": "Vaše data",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Potvrdit odběr",
|
||||
"email.optin.confirmSubHelp": "Potvrďte svůj odběr klepnutím na níže uvedené tlačítko.",
|
||||
"email.optin.confirmSubInfo": "Byli jste přidáni do těchto seznamů:",
|
||||
"email.optin.confirmSubHelp": "Potvrďte svůj odběr kliknutím na níže uvedené tlačítko.",
|
||||
"email.optin.confirmSubInfo": "Byli jste přidáni do následujících seznamů:",
|
||||
"email.optin.confirmSubTitle": "Potvrdit odběr",
|
||||
"email.optin.confirmSubWelcome": "Zdravím",
|
||||
"email.optin.privateList": "Soukromý seznam",
|
||||
"email.status.campaignReason": "Příčina",
|
||||
"email.status.campaignReason": "Důvod",
|
||||
"email.status.campaignSent": "Odesláno",
|
||||
"email.status.campaignUpdateTitle": "Aktualizace kampaně",
|
||||
"email.status.importFile": "Soubor",
|
||||
@@ -127,10 +130,10 @@
|
||||
"email.unsubHelp": "Nechcete dostávat tyto e-maily?",
|
||||
"email.viewInBrowser": "Zobrazit v prohlížeči",
|
||||
"forms.formHTML": "HTML formuláře",
|
||||
"forms.formHTMLHelp": "Použijte následující HTML k zobrazení formuláře odběru na externí webové stránce. Formulář by měl mít pole e-mailu a jedno nebo více polí `l` (vypsat UUID). Název pole je volitelný.",
|
||||
"forms.noPublicLists": "Nejsou žádné veřejné seznamy ke generování formulářů.",
|
||||
"forms.formHTMLHelp": "Použijte následující HTML k zobrazení formuláře odběru na externí webové stránce. Formulář by měl mít pole e-mailu a jedno nebo více polí `l` (s hodnotou UUID seznamu). Pole jména je volitelné.",
|
||||
"forms.noPublicLists": "Nejsou k dispozici žádné veřejné seznamy pro vytvoření formuláře.",
|
||||
"forms.publicLists": "Veřejné seznamy",
|
||||
"forms.publicSubPage": "Veřejná stránka odběru",
|
||||
"forms.publicSubPage": "Veřejná stránka přihlášení k odběru",
|
||||
"forms.selectHelp": "Vyberte seznamy k přidání do formuláře.",
|
||||
"forms.title": "Formuláře",
|
||||
"globals.buttons.add": "Přidat",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopírovat",
|
||||
"globals.buttons.delete": "Odstranit",
|
||||
"globals.buttons.deleteAll": "Odstranit vše",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Upravit",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Povoleno",
|
||||
"globals.buttons.insert": "Vložit",
|
||||
"globals.buttons.learnMore": "Další informace",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Neplatné pole: {name}",
|
||||
"globals.messages.invalidID": "Neplatné ID",
|
||||
"globals.messages.invalidUUID": "Neplatné UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Chybějící pole: {name}",
|
||||
"globals.messages.notFound": "{name} nebyl nalezen",
|
||||
"globals.messages.numSelected": "{num} vybráno",
|
||||
"globals.messages.passwordChange": "Zadejte hodnotu ke změně",
|
||||
"globals.messages.passwordChangeFull": "Vymazat a zadat úplné heslo znovu v '{name}'.",
|
||||
"globals.messages.permissionDenied": "Odmítnuto oprávnění: {name}",
|
||||
@@ -209,7 +216,7 @@
|
||||
"globals.months.4": "Dub",
|
||||
"globals.months.5": "Kvě",
|
||||
"globals.months.6": "Čer",
|
||||
"globals.months.7": "Črc",
|
||||
"globals.months.7": "Čvc",
|
||||
"globals.months.8": "Srp",
|
||||
"globals.months.9": "Zář",
|
||||
"globals.states.off": "Vypnout",
|
||||
@@ -236,12 +243,13 @@
|
||||
"globals.terms.settings": "Nastavení",
|
||||
"globals.terms.subscriber": "Odběratel | Odběratelé",
|
||||
"globals.terms.subscribers": "Odběratelé",
|
||||
"globals.terms.subscriptions": "Přihlášení",
|
||||
"globals.terms.subscriptions": "Odběr | Odběry",
|
||||
"globals.terms.tag": "Značka | Značky",
|
||||
"globals.terms.tags": "Značky",
|
||||
"globals.terms.template": "Šablona | Šablony",
|
||||
"globals.terms.templates": "Šablony",
|
||||
"globals.terms.tx": "Transakční | Transakční",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Uživatel | Uživatelé",
|
||||
"globals.terms.users": "Uživatelé",
|
||||
"globals.terms.year": "Rok | Roky",
|
||||
@@ -249,9 +257,9 @@
|
||||
"import.blocklist": "Seznam blokovaných",
|
||||
"import.csvDelim": "Oddělovač CSV",
|
||||
"import.csvDelimHelp": "Výchozí oddělovač je čárka.",
|
||||
"import.csvExample": "Vzorový prvotní CSV",
|
||||
"import.csvExample": "Ukázkové CSV (raw)",
|
||||
"import.csvFile": "Soubor CSV nebo ZIP",
|
||||
"import.csvFileHelp": "Klepněte nebo přetáhněte soubor CSV nebo ZIP sem",
|
||||
"import.csvFileHelp": "Klikněte nebo přetáhněte soubor CSV nebo ZIP sem",
|
||||
"import.errorCopyingFile": "Chyba při kopírování souboru: {error}",
|
||||
"import.errorProcessingZIP": "Chyba při zpracování souboru ZIP: {error}",
|
||||
"import.errorStarting": "Chyba při spuštění importu: {error}",
|
||||
@@ -271,7 +279,7 @@
|
||||
"import.recordsCount": "{num} / {total} záznamů",
|
||||
"import.stopImport": "Zastavit import ",
|
||||
"import.subscribe": "Odebírat",
|
||||
"import.subscribeWarning": "Přepsání přibere zpět xxx neodebrané e-maily. Pokračovat?",
|
||||
"import.subscribeWarning": "Přepsání znovu přihlásí odhlášené adresy. Pokračovat?",
|
||||
"import.title": "Importovat odběratele",
|
||||
"import.upload": "Odeslat",
|
||||
"lists.confirmDelete": "Jste si jisti? Tímto se neodstraní odběratelé.",
|
||||
@@ -284,7 +292,7 @@
|
||||
"lists.optins.double": "Přihlášení k odběru s potvrzením",
|
||||
"lists.optins.single": "Jednotlivé přihlášení k odběru",
|
||||
"lists.sendCampaign": "Odeslat kampaň",
|
||||
"lists.sendOptinCampaign": "Odeslat kampaň dle přihlášení k odběru",
|
||||
"lists.sendOptinCampaign": "Odeslat potvrzovací kampaň pro přihlášení",
|
||||
"lists.type": "Typ",
|
||||
"lists.typeHelp": "Veřejné seznamy jsou celosvětově přístupné k odběru a jejich názvy se mohou objevit na veřejných stránkách, jako je stránka pro správu odběrů.",
|
||||
"lists.types.private": "Soukromý",
|
||||
@@ -293,7 +301,7 @@
|
||||
"maintenance.help": "Některé operace mohou trvat déle v závislosti na množství dat.",
|
||||
"maintenance.maintenance.unconfirmedOptins": "Nepotvrzené opt-in přihlášení",
|
||||
"maintenance.olderThan": "Starší než",
|
||||
"maintenance.orphanHelp": "Sirotci = předplatitelé bez seznamů",
|
||||
"maintenance.orphanHelp": "Sirotci = Odběratelé bez přiřazených seznamů",
|
||||
"maintenance.title": "Údržba",
|
||||
"maintenance.unconfirmedSubs": "Nepotvrzená přihlášení starší než {name} dnů.",
|
||||
"media.errorReadingFile": "Chyba při čtení souboru: {error}",
|
||||
@@ -303,9 +311,8 @@
|
||||
"media.invalidFile": "Neplatný soubor: {error}",
|
||||
"media.title": "Médium",
|
||||
"media.unsupportedFileType": "Nepodporovaný typ souboru ({type})",
|
||||
"media.upload": "Odeslat",
|
||||
"media.uploadHelp": "Klepněte nebo přetáhněte jeden nebo více obrázků sem",
|
||||
"media.uploadImage": "Odeslat obrázek",
|
||||
"media.upload": "Nahrát",
|
||||
"media.uploadHelp": "Klikněte nebo přetáhněte jeden nebo více obrázků sem",
|
||||
"menu.allCampaigns": "Všechny kampaně",
|
||||
"menu.allLists": "Všechny seznamy",
|
||||
"menu.allSubscribers": "Všichni odběratelé",
|
||||
@@ -318,8 +325,8 @@
|
||||
"menu.newCampaign": "Vytvořit nový",
|
||||
"menu.settings": "Nastavení",
|
||||
"public.archiveEmpty": "Žádné archivované zprávy.",
|
||||
"public.archiveTitle": "Archiv poštovních seznamů",
|
||||
"public.blocklisted": "Trvale odhlášen.",
|
||||
"public.archiveTitle": "Archiv kampaní",
|
||||
"public.blocklisted": "Trvale odhlášen z odběru.",
|
||||
"public.campaignNotFound": "E-mailová zpráva nebyla nalezena.",
|
||||
"public.confirmOptinSubTitle": "Potvrdit odběr",
|
||||
"public.confirmSub": "Potvrdit odběr",
|
||||
@@ -337,8 +344,8 @@
|
||||
"public.invalidCaptcha": "Neplatný CAPTCHA.",
|
||||
"public.invalidFeature": "Tato funkce není k dispozici.",
|
||||
"public.invalidLink": "Neplatný odkaz",
|
||||
"public.managePrefs": "Zpráva předvoleb",
|
||||
"public.managePrefsUnsub": "Zrušit výběr seznamu pro odhlášení.",
|
||||
"public.managePrefs": "Správa předvoleb",
|
||||
"public.managePrefsUnsub": "Zrušte zaškrtnutí seznamů, ze kterých se chcete odhlásit.",
|
||||
"public.noListsAvailable": "Nejsou k dispozici žádné seznamy k odběru.",
|
||||
"public.noListsSelected": "Nebyly vybrány žádné platné seznamy k odběru.",
|
||||
"public.noSubInfo": "Nejsou zde žádné odběry k potvrzení.",
|
||||
@@ -361,18 +368,18 @@
|
||||
"public.subPrivateList": "Soukromý seznam",
|
||||
"public.subTitle": "Odebírat",
|
||||
"public.unsub": "Zrušit odběr",
|
||||
"public.unsubFull": "Zrušte odběr rovněž ze všech budoucích e-mailů.",
|
||||
"public.unsubFull": "Zrušit odběr i ze všech budoucích e-mailů.",
|
||||
"public.unsubHelp": "Chcete zrušit odběr z tohoto seznamu adresátů?",
|
||||
"public.unsubTitle": "Zrušit odběr",
|
||||
"public.unsubbedInfo": "Odběr jste zrušili úspěšně.",
|
||||
"public.unsubbedTitle": "Zrušen odběr",
|
||||
"public.unsubbedInfo": "Odběr byl úspěšně zrušen.",
|
||||
"public.unsubbedTitle": "Odběr zrušen",
|
||||
"public.unsubscribeTitle": "Zrušit odběr ze seznamu adresátů",
|
||||
"settings.appearance.adminHelp": "Volitelné CSS aplikované na admin UI.",
|
||||
"settings.appearance.adminName": "Admin",
|
||||
"settings.appearance.customCSS": "Volitelný CSS",
|
||||
"settings.appearance.customCSS": "Volitelné CSS",
|
||||
"settings.appearance.customJS": "Volitelný JavaScript",
|
||||
"settings.appearance.name": "Vzhled",
|
||||
"settings.appearance.publicHelp": "VOlitelné CSS a JavaScript aplikované na veřejné stránky.",
|
||||
"settings.appearance.publicHelp": "Volitelné CSS a JavaScript aplikované na veřejné stránky.",
|
||||
"settings.appearance.publicName": "Veřejné",
|
||||
"settings.bounces.action": "Akce",
|
||||
"settings.bounces.blocklist": "Seznam blokovaných",
|
||||
@@ -408,29 +415,29 @@
|
||||
"settings.general.adminNotifEmailsHelp": "Seznam e-mailových adres oddělených čárkami, na které by se měla odeslat oznámení administrátora, jako jsou aktualizace importu, dokončení kampaní, selhání atd.",
|
||||
"settings.general.checkUpdates": "Kontrola aktualizací",
|
||||
"settings.general.checkUpdatesHelp": "Pravidelně kontrolovat nová vydání aplikace a upozornit.",
|
||||
"settings.general.enablePublicArchive": "Enable public mailing list archive page",
|
||||
"settings.general.enablePublicArchiveHelp": "Zveřejnit kampaně, pro které je povolena archivace na veřejné web stránce.",
|
||||
"settings.general.enablePublicArchive": "Povolit veřejný archiv kampaní",
|
||||
"settings.general.enablePublicArchiveHelp": "Zveřejnit kampaně, pro které je povolena archivace na veřejné webové stránce.",
|
||||
"settings.general.enablePublicArchiveRSSContent": "Zobrazovat celý obsah v RSS feedu",
|
||||
"settings.general.enablePublicArchiveRSSContentHelp": "Zobrazovat celý obsah e-mailu v RSS feedu. Pokud je deaktivováno, budou zobrazeny pouze název a odkazy.",
|
||||
"settings.general.enablePublicSubPage": "Povolit veřejnou stránku odběru",
|
||||
"settings.general.enablePublicSubPageHelp": "Zobrazit veřejnou stránku odběru se všemi veřejnými seznamy pro lidi k odběru.",
|
||||
"settings.general.faviconURL": "Adresa URL ikony favicon",
|
||||
"settings.general.faviconURLHelp": "(Volitelné) Úplná adresa URL k zobrazení statické ikony favicon na pohledu zaměřeném na uživatele, jako je stránka pro zrušení odběru.",
|
||||
"settings.general.faviconURLHelp": "(Volitelné) Úplná URL statické favicon pro veřejné stránky. (např. stránka pro zrušení odběru).",
|
||||
"settings.general.fromEmail": "Výchozí e-mail `od`",
|
||||
"settings.general.fromEmailHelp": "Výchozí e-mail `od` k zobrazení odchozích e-mailů kampaní. Lze změnit podle kampaně.",
|
||||
"settings.general.language": "Jazyk",
|
||||
"settings.general.logoURL": "Adresa URL loga",
|
||||
"settings.general.logoURLHelp": "(Volitelné) Úplná adresa URL k zobrazení statického loga na pohledu zaměřeném na uživatele, jako je stránka pro zrušení odběru.",
|
||||
"settings.general.logoURLHelp": "(Volitelné) Úplná URL statického loga pro veřejné stránky (např. stránka pro zrušení odběru).",
|
||||
"settings.general.name": "Obecné",
|
||||
"settings.general.rootURL": "Kořenová adresa URL",
|
||||
"settings.general.rootURLHelp": "Veřejná adresa URL instalace (bez koncového lomítka).",
|
||||
"settings.general.sendOptinConfirm": "Odeslat souhlas s odběrem",
|
||||
"settings.general.sendOptinConfirmHelp": "Odeslat e-mail se souhlasem po přihlášení nebo přidání nových odběratelů na admin formuláři.",
|
||||
"settings.general.siteName": "Jméno stránky",
|
||||
"settings.general.siteName": "Název stránky",
|
||||
"settings.invalidMessengerName": "Neplatné jméno kurýra.",
|
||||
"settings.mailserver.authProtocol": "Ověřovací protokol",
|
||||
"settings.mailserver.host": "Hostitel",
|
||||
"settings.mailserver.hostHelp": "Adresa hostitele serveru SMTP.",
|
||||
"settings.mailserver.hostHelp": "Adresa hostitele SMTP serveru.",
|
||||
"settings.mailserver.idleTimeout": "Časový limit nečinnosti",
|
||||
"settings.mailserver.idleTimeoutHelp": "Doba čekání na novou aktivitu na připojení před uzavřením a odebráním z fondu (s - sekundy, m - minuty).",
|
||||
"settings.mailserver.maxConns": "Maximální počet připojení",
|
||||
@@ -439,43 +446,43 @@
|
||||
"settings.mailserver.password": "Heslo",
|
||||
"settings.mailserver.passwordHelp": "Klávesou Enter zadejte změnu",
|
||||
"settings.mailserver.port": "Port",
|
||||
"settings.mailserver.portHelp": "Port serveru SMTP.",
|
||||
"settings.mailserver.portHelp": "Port SMTP serveru.",
|
||||
"settings.mailserver.skipTLS": "Přeskočit ověření TLS",
|
||||
"settings.mailserver.skipTLSHelp": "Přeskočit kontrolu názvu hostitele na certifikát TLS.",
|
||||
"settings.mailserver.skipTLSHelp": "Přeskočit kontrolu názvu hostitele v TLS certifikátu.",
|
||||
"settings.mailserver.tls": "TLS",
|
||||
"settings.mailserver.tlsHelp": "Povolit STARTTLS.",
|
||||
"settings.mailserver.tlsHelp": "Šifrování TLS/SSL. Běžně se používá STARTTLS.",
|
||||
"settings.mailserver.username": "Jméno uživatele",
|
||||
"settings.mailserver.waitTimeout": "Časový limit čekání",
|
||||
"settings.mailserver.waitTimeoutHelp": "Doba čekání na novou aktivitu na připojení před uzavřením a odebráním z fondu (s - sekundy, m - minuty).",
|
||||
"settings.maintenance.cron": "Interval Cron",
|
||||
"settings.media.provider": "Poskytovatel",
|
||||
"settings.media.s3.bucket": "Sektor",
|
||||
"settings.media.s3.bucketPath": "Cesta sektoru",
|
||||
"settings.media.s3.bucketPathHelp": "Cesta uvnitř sektoru k odeslání souborů. Výchozí je /",
|
||||
"settings.media.s3.bucketType": "Typ sektoru",
|
||||
"settings.media.s3.bucket": "Bucket",
|
||||
"settings.media.s3.bucketPath": "Cesta v bucketu",
|
||||
"settings.media.s3.bucketPathHelp": "Cesta uvnitř bucketu, kam se budou nahrávat soubory. Výchozí je /.",
|
||||
"settings.media.s3.bucketType": "Typ bucketu",
|
||||
"settings.media.s3.bucketTypePrivate": "Soukromý",
|
||||
"settings.media.s3.bucketTypePublic": "Veřejný",
|
||||
"settings.media.s3.key": "Přístupový klíč AWS",
|
||||
"settings.media.s3.publicURL": "Volitelné veřejné URL",
|
||||
"settings.media.s3.publicURLHelp": "Zákaznická doména S3 použitá k pro odkazy na obrázky náhradou za předvolené S3 URL.",
|
||||
"settings.media.s3.publicURLHelp": "Vlastní S3 doména pro odkazy na obrázky místo výchozí backendové URL S3.",
|
||||
"settings.media.s3.region": "Oblast",
|
||||
"settings.media.s3.secret": "Přístupový tajný údaj AWS",
|
||||
"settings.media.s3.uploadExpiry": "Uplynulá platnost odeslání",
|
||||
"settings.media.s3.uploadExpiryHelp": "(Volitelné) Uveďte TTL pro generovanou předem přihlášenou adresu URL. Vhodné pouze pro soukromé sektory (s, m, h, d pro sekundy, minuty, hodiny, dny).",
|
||||
"settings.media.s3.uploadExpiry": "Platnost nahrávání",
|
||||
"settings.media.s3.uploadExpiryHelp": "(Volitelné) Uveďte TTL pro generovanou předem přihlášenou adresu URL. Vhodné pouze pro soukromé buckety (s, m, h, d pro sekundy, minuty, hodiny, dny).",
|
||||
"settings.media.s3.url": "Adresa URL pro S3 backend",
|
||||
"settings.media.s3.urlHelp": "Lze změnit, pouze pokud se použije S3 kompatibilní backend, jako je Minio.",
|
||||
"settings.media.title": "Odeslání médií",
|
||||
"settings.media.title": "Nahrávání médií",
|
||||
"settings.media.upload.extensions": "Povolené přípony souborů",
|
||||
"settings.media.upload.extensionsHelp": "Přidejte * pro povolení všech přípon",
|
||||
"settings.media.upload.path": "Cesta odeslání",
|
||||
"settings.media.upload.pathHelp": "Cesta k adresáři, kam se odešlou média.",
|
||||
"settings.media.upload.uri": "URI odeslání",
|
||||
"settings.media.upload.uriHelp": "URI odeslání viditelný vnějšímu světu. Média odeslaná do cesty_k_odeslání budou veřejně přístupná pod adresou {root_url}, např. https://listmonk.yoursite.com/uploads.",
|
||||
"settings.media.upload.path": "Cesta pro nahrávání",
|
||||
"settings.media.upload.pathHelp": "Cesta k adresáři, do kterého se budou nahrávat média.",
|
||||
"settings.media.upload.uri": "Adresa pro nahrávání (URI)",
|
||||
"settings.media.upload.uriHelp": "Adresa (URI) pro nahrávání, která je dostupná z internetu. Média nahraná do cesty_k_nahrání budou veřejně přístupná pod adresou {root_url}, například https://listmonk.yoursite.com/uploads.",
|
||||
"settings.messengers.maxConns": "Maximální počet připojení",
|
||||
"settings.messengers.maxConnsHelp": "Maximální počet souběžných připojení k serveru.",
|
||||
"settings.messengers.messageSaved": "Nastavení uloženo. Znovu se načítá aplikace...",
|
||||
"settings.messengers.name": "Odesílatelé",
|
||||
"settings.messengers.nameHelp": "např.: my-sms. Alfanumerika / pomlčka.",
|
||||
"settings.messengers.nameHelp": "např.: my-sms. Alfa-numerické znaky / pomlčka.",
|
||||
"settings.messengers.password": "Heslo",
|
||||
"settings.messengers.retries": "Opakování",
|
||||
"settings.messengers.retriesHelp": "Počet opakovaných pokusů, když zpráva selže.",
|
||||
@@ -483,7 +490,7 @@
|
||||
"settings.messengers.timeout": "Časový limit nečinnosti",
|
||||
"settings.messengers.timeoutHelp": "Doba čekání na novou aktivitu na připojení před uzavřením a odebráním z fondu (s - sekundy, m - minuty).",
|
||||
"settings.messengers.url": "URL",
|
||||
"settings.messengers.urlHelp": "Kořenová adresa URL serveru Postback.",
|
||||
"settings.messengers.urlHelp": "Kořenová URL postback serveru.",
|
||||
"settings.messengers.username": "Jméno uživatele",
|
||||
"settings.needsRestart": "Nastavení změněno. Pozastavte všechny spuštěné kampaně a restartujte aplikaci",
|
||||
"settings.performance.batchSize": "Velikost dávky",
|
||||
@@ -498,39 +505,48 @@
|
||||
"settings.performance.messageRateHelp": "Maximální počet zpráv, které se mají odeslat za sekundu na modul worker za sekundu. Jestliže souběžnost = 10 a četnost_zpráv = 10, pak je možné každou sekundu odeslat až 10x10=100 zpráv. Toto, spolu se souběžností, by mělo platit, aby se zachovalo vysílání síťových zpráv za sekundu pod limity četnosti zpráv na cílových serverech, pokud jsou nastaveny.",
|
||||
"settings.performance.name": "Výkon",
|
||||
"settings.performance.slidingWindow": "Povolit limit posuvného okna",
|
||||
"settings.performance.slidingWindowDuration": "Doba trvání",
|
||||
"settings.performance.slidingWindowDuration": "Doba trvání posuvného okna",
|
||||
"settings.performance.slidingWindowDurationHelp": "Doba trvání období posuvného okna (m - minuty, h - hodiny).",
|
||||
"settings.performance.slidingWindowHelp": "Limit celkového počtu zpráv odeslaných za dané období. Při dosažení tohoto limitu se zadrží odesílání zpráv, dokud se časové okno nevymaže.",
|
||||
"settings.performance.slidingWindowHelp": "Limit celkového počtu odeslaných zpráv za dané období. Po dosažení limitu se odesílání pozastaví, dokud časové okno nevyprší.",
|
||||
"settings.performance.slidingWindowRate": "Maximální počet zpráv",
|
||||
"settings.performance.slidingWindowRateHelp": "Maximální počet zpráv k odeslání v rámci doby trvání okna.",
|
||||
"settings.privacy.allowBlocklist": "Povolit stanovení seznamu blokovaných",
|
||||
"settings.privacy.allowBlocklistHelp": "Povolit odběratelům zrušit odběr ze všech seznamů adresářů a označit sebe jako blokované?",
|
||||
"settings.privacy.allowBlocklist": "Povolit zařazení na seznam blokovaných",
|
||||
"settings.privacy.allowBlocklistHelp": "Povolit odběratelům odhlásit se ze všech seznamů a označit svou adresu jako zablokovanou?",
|
||||
"settings.privacy.allowExport": "Umožnit export",
|
||||
"settings.privacy.allowExportHelp": "Umožnit odběratelům exportovat shromážděná data?",
|
||||
"settings.privacy.allowPrefs": "Povolit změnu předvoleb",
|
||||
"settings.privacy.allowPrefsHelp": "Povolit přihlášeným změnu předvoleb jako jsou jména a přihlášení k více seznamům.",
|
||||
"settings.privacy.allowWipe": "Umožnit vymazání",
|
||||
"settings.privacy.allowWipeHelp": "Umožnit odběratelům odstranit sebe včetně svých odběrů a všech ostatních dat z databáze. Pohledy na kampaně a klepnutí na odkazy se rovněž odeberou, zatímco pohledy a počty klepnutí se zachovají (aniž by měly přidruženého odběratele), takže statistiky a analýzy nebudou ovlivněny.",
|
||||
"settings.privacy.allowWipeHelp": "Umožnit odběratelům odstranit sebe včetně svých odběrů a všech ostatních dat z databáze. Pohledy na kampaně a kliknutí na odkazy se rovněž odeberou, zatímco pohledy a počty kliknutí se zachovají (aniž by měly přidruženého odběratele), takže statistiky a analýzy nebudou ovlivněny.",
|
||||
"settings.privacy.domainAllowlist": "Povolené domény",
|
||||
"settings.privacy.domainAllowlistHelp": "Přihlásit se mohou pouze e-mailové adresy s těmito doménami. Zadejte jednu doménu na řádek, např.: example.com, *.example.com",
|
||||
"settings.privacy.domainBlocklist": "Seznam blokovaných domén",
|
||||
"settings.privacy.domainBlocklistHelp": "E-mailové adresy z těchto domén se nemohou přihlásit k odběru. Uveďte jednu doménu na řádek, eg: somesite.com",
|
||||
"settings.privacy.domainBlocklistHelp": "E-mailové adresy z těchto domén se nemohou přihlásit k odběru. Uveďte jednu doménu na řádek, např.: example.com",
|
||||
"settings.privacy.individualSubTracking": "Sledování jednotlivých odběratelů",
|
||||
"settings.privacy.individualSubTrackingHelp": "Sledovat klepnutí a pohledy na kampaně na úrovni odběratelů. Je-li to zakázáno, sledování klepnutí a pohledů pokračuje, aniž by bylo propojeno s jednotlivými odběrateli.",
|
||||
"settings.privacy.individualSubTrackingHelp": "Sledovat kliknutí a pohledy na kampaně na úrovni odběratelů. Je-li to zakázáno, sledování kliknutí a pohledů pokračuje, aniž by bylo propojeno s jednotlivými odběrateli.",
|
||||
"settings.privacy.listUnsubHeader": "Zahrnout záhlaví `List-Unsubscribe`",
|
||||
"settings.privacy.listUnsubHeaderHelp": "Zahrnout záhlaví zrušení odběrů, která umožňují e-mailovým klientům, aby povolili uživatelům zrušit odběr jediným klepnutím.",
|
||||
"settings.privacy.listUnsubHeaderHelp": "Zahrnout záhlaví zrušení odběrů, která umožňují e-mailovým klientům, aby povolili uživatelům zrušit odběr jediným kliknutím.",
|
||||
"settings.privacy.name": "Soukromí",
|
||||
"settings.privacy.recordOptinIP": "Zaznamenávat IP adresy pro opt-in",
|
||||
"settings.privacy.recordOptinIPHelp": "Zaznamenávat IP adresy pro dvojí opt-in v atributu odběratele.",
|
||||
"settings.restart": "Restartovat",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Automaticky vytvořit uživatele",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Automaticky vytvořit uživatele při prvním přihlášení, pokud účet neexistuje.",
|
||||
"settings.security.OIDCClientID": "ID klienta",
|
||||
"settings.security.OIDCClientSecret": "Tajný klíč klienta",
|
||||
"settings.security.OIDCDefaultListRole": "Výchozí role v seznamu",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Výchozí role přiřazená uživatelům automaticky vytvořeným z OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Výchozí uživatelská role",
|
||||
"settings.security.OIDCHelp": "Povolit přihlášení OpenID Connect OAuth2 pomocí poskytovatele OAuth.",
|
||||
"settings.security.OIDCName": "Název poskytovatele",
|
||||
"settings.security.OIDCRedirectURL": "URL přesměrování pro poskytovatele oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Toto se nezdá být výrobní URL. Změňte kořenové URL v nastavení 'Obecné'.",
|
||||
"settings.security.OIDCRedirectWarning": "Toto nevypadá jako produkční URL. Změňte kořenovou URL v \"Obecném\" nastavení.",
|
||||
"settings.security.OIDCURL": "URL poskytovatele",
|
||||
"settings.security.OIDCWarning": "Pokud je povoleno OIDC, výchozí přihlášení heslem je zakázáno. Neplatná konfigurace může vést k uzamčení.",
|
||||
"settings.security.altchaComplexity": "Složitost Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Vyšší hodnoty poskytují lepší zabezpečení, ale pomalejší řešení (1000-1000000).",
|
||||
"settings.security.captchaKey": "Klíč z hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Navštivte www.hcaptcha.com pro získání klíče a tajného kódu.",
|
||||
"settings.security.captchaSecret": "Tajný kód z hCaptcha.com",
|
||||
@@ -542,7 +558,7 @@
|
||||
"settings.smtp.customHeadersHelp": "Volitelné pole e-mailových záhlaví, která se mají zahrnout do všech zpráv odeslaných z tohoto serveru. Např.: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"settings.smtp.enabled": "Povoleno",
|
||||
"settings.smtp.heloHost": "Název hostitele HELO",
|
||||
"settings.smtp.heloHostHelp": "Volitelné. Některé servery SMTP požadují úplný název domény v názvu hostitele. Standardně se HELLO pojí s `localhost`. Nastavte, pokud by se měl použít vlastní název hostitele.",
|
||||
"settings.smtp.heloHostHelp": "(Volitelné) Některé SMTP servery vyžadují úplný název domény (FQDN) v názvu hostitele. Ve výchozím nastavení se v příkazu HELO používá `localhost`. Nastavte, pokud má být použit vlastní hostname.",
|
||||
"settings.smtp.name": "SMTP",
|
||||
"settings.smtp.retries": "Opakování",
|
||||
"settings.smtp.retriesHelp": "Počet opakovaných pokusů, když zpráva selže.",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Na e-mail",
|
||||
"settings.title": "Nastavení",
|
||||
"settings.updateAvailable": "Nová aktualizace {version} je k dispozici.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Rozšířené",
|
||||
"subscribers.advancedQueryHelp": "Dílčí výraz SQL k dotazu na atributy odběratele",
|
||||
"subscribers.attribs": "Atributy",
|
||||
@@ -577,21 +594,21 @@
|
||||
"subscribers.invalidName": "Neplatné jméno.",
|
||||
"subscribers.listChangeApplied": "Změna seznamu použita.",
|
||||
"subscribers.lists": "Seznamy",
|
||||
"subscribers.listsHelp": "Seznamy, ze kterých nelze odebrat odběratele, kteří zrušili sami sobě odběr.",
|
||||
"subscribers.listsHelp": "Seznamy, u nichž si odběratelé sami zrušili odběr, nelze odebrat.",
|
||||
"subscribers.listsPlaceholder": "Seznamy k odběru",
|
||||
"subscribers.manageLists": "Spravovat seznamy",
|
||||
"subscribers.markUnsubscribed": "Označit jako zrušený odběr",
|
||||
"subscribers.newSubscriber": "Nový odběratel",
|
||||
"subscribers.numSelected": "{num} vybraných odběratelů",
|
||||
"subscribers.optinSubject": "Potvrdit odběr",
|
||||
"subscribers.preconfirm": "Před-potvrdit odběr",
|
||||
"subscribers.preconfirmHelp": "Neodesílat souhlas s kontaktováním a označit všechny e-maily v seznamu jako 'Odebíráno'.",
|
||||
"subscribers.preconfirm": "Předem potvrdit odběr",
|
||||
"subscribers.preconfirmHelp": "Neodesílat potvrzovací e-maily a označit všechny odběry jako 'přihlášené'.",
|
||||
"subscribers.query": "Dotaz",
|
||||
"subscribers.queryPlaceholder": "E-mail nebo jméno",
|
||||
"subscribers.reset": "Vynulovat",
|
||||
"subscribers.selectAll": "Vybrat vše {num}",
|
||||
"subscribers.sendOptinConfirm": "Odeslat souhlas s kontaktováním",
|
||||
"subscribers.sentOptinConfirm": "Souhlas s kontaktováním odeslán",
|
||||
"subscribers.sendOptinConfirm": "Odeslat potvrzení přihlášení (opt-in)",
|
||||
"subscribers.sentOptinConfirm": "Potvrzení přihlášení (opt-in) odesláno",
|
||||
"subscribers.status.blocklisted": "Uvedeno na seznamu blokovaných",
|
||||
"subscribers.status.confirmed": "Potvrzeno",
|
||||
"subscribers.status.enabled": "Povoleno",
|
||||
@@ -618,11 +635,14 @@
|
||||
"users.apiOneTimeToken": "Zkopírujte přístupový token k API nyní. Nebude znovu zobrazen.",
|
||||
"users.cantDeleteRole": "Nelze smazat roli, která je používána.",
|
||||
"users.firstTime": "Toto je čerstvá instalace. Vyberte si uživatelské jméno a heslo pro účet Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Neplatné přihlášení nebo heslo",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Neplatný požadavek ověření",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Poslední přihlášení",
|
||||
"users.listPerms": "Oprávnění k seznamům",
|
||||
"users.listPermsWarning": "lists:get_all nebo lists:manage_all jsou povolené a přepisují tyto oprávnění",
|
||||
"users.listPermsWarning": "Oprávnění lists:get_all nebo lists:manage_all jsou povolená a přepisují oprávnění nastavená pro jednotlivé seznamy.",
|
||||
"users.listRole": "Role ve seznamu | Role ve seznamu",
|
||||
"users.listRoles": "Role ve seznamu",
|
||||
"users.login": "Přihlásit",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Odhlásit",
|
||||
"users.needSuper": "Uživatel(y) nelze aktualizovat. Musí být alespoň jeden aktivní uživatel se super administračními právy.",
|
||||
"users.newListRole": "Nová role seznamu",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nový uživatel",
|
||||
"users.newUserRole": "Nová uživatelská role",
|
||||
"users.password": "Heslo",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Opakujte heslo",
|
||||
"users.perms": "Oprávnění",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Role | Role",
|
||||
"users.roleGroup": "Skupina",
|
||||
"users.roles": "Role",
|
||||
"users.status.disabled": "Deaktivované",
|
||||
"users.status.enabled": "Povolené",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Typ",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/cy.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Amddifad",
|
||||
"email.data.info": "Mae copi o'r data sydd wedi'u cadw amdanoch chi wedi'i atodi fel ffeil JSON. Gallwch edrych ar y ffeil mewn golygydd testun.",
|
||||
"email.data.title": "Eich data",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Cadarnhau tanysgrifiad",
|
||||
"email.optin.confirmSubHelp": "Cadarnhewch eich tanysgrifiad drwy glicio'r botwm isod",
|
||||
"email.optin.confirmSubInfo": "Rydych chi wedi cael eich ychwanegu at y rhestrau canlynol:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copïwch",
|
||||
"globals.buttons.delete": "Dileu",
|
||||
"globals.buttons.deleteAll": "Dileu'r cyfan",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Golygu",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Wedi galluogi",
|
||||
"globals.buttons.insert": "Mewnosod",
|
||||
"globals.buttons.learnMore": "Dysgu mwy",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Meysydd annilys: {name}",
|
||||
"globals.messages.invalidID": "ID annilys",
|
||||
"globals.messages.invalidUUID": "UUID annilys",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Maes/meysydd coll: {name}",
|
||||
"globals.messages.notFound": "Heb ddod o hyd i {enw]",
|
||||
"globals.messages.numSelected": "{num} wedi'u dewis",
|
||||
"globals.messages.passwordChange": "Rhoi gwerth i'w newid",
|
||||
"globals.messages.passwordChangeFull": "Clirio ac ailgyflwyno'r cyfrinair llawn yn '{name}'.",
|
||||
"globals.messages.permissionDenied": "Gwrthodwr cydrannau: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Templed | Templedi",
|
||||
"globals.terms.templates": "Templedi",
|
||||
"globals.terms.tx": "Trafodion",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Defnyddiwr | Defnyddwyr",
|
||||
"globals.terms.users": "Defnyddwyr",
|
||||
"globals.terms.year": "Blwyddyn | Blynyddoedd",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Math o ffeil nad yw'n cael ei gefnogi ({type})",
|
||||
"media.upload": "Llwytho i fyny",
|
||||
"media.uploadHelp": "Clicio neu lusgo un llun neu fwy yma",
|
||||
"media.uploadImage": "Llwytho llun i fyny",
|
||||
"menu.allCampaigns": "Pob ymgyrch",
|
||||
"menu.allLists": "Pob rhestr",
|
||||
"menu.allSubscribers": "Pob tanysgrifiwr",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Cofnodi cyfeiriad IP dewis mewn",
|
||||
"settings.privacy.recordOptinIPHelp": "Cofnodi cyfeiriad IP ar bwyntio dwbl yn manylion tanysgrifiwr.",
|
||||
"settings.restart": "Ailgychwyn",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Creu defnyddwyr yn awtomatig",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Creu defnyddiwr yn awtomatig ar y mewngofnodi cyntaf os nad yw'r cyfrif yn bodoli.",
|
||||
"settings.security.OIDCClientID": "ID Cleient",
|
||||
"settings.security.OIDCClientSecret": "Cyfrinach Cleient",
|
||||
"settings.security.OIDCDefaultListRole": "Rôl rhestr ddiofyn",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Rôl ddiofyn a ychwanegir i ddefnyddwyr a grëwyd yn awtomatig o OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Rôl defnyddiwr ddiofyn",
|
||||
"settings.security.OIDCHelp": "Galluogi mewngofnodi OAuth2 Connect OpenID Connect drwy ddarparwr OAuth.",
|
||||
"settings.security.OIDCName": "Enw'r darparwr",
|
||||
"settings.security.OIDCRedirectURL": "URL ailgyfeirio ar gyfer darparwr oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Nid yw hwn yn ymddangos fel URL cynhyrchu. Newidiwch y URL Gwraidd yn y gosodiadau 'Cyffredinol'.",
|
||||
"settings.security.OIDCURL": "URL Darparwr",
|
||||
"settings.security.OIDCWarning": "Pan gaiff OIDC ei alluogi, mewngofnodi â chyfrinair diofyn yn cael ei analluogi. Gellir eich cloi yn gyfan gwbl os yw'r cyfluniad yn annilys.",
|
||||
"settings.security.altchaComplexity": "Cymhlethdod Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Mae gwerthoedd uwch yn cynnig gwell diogelwch ond datrys yn arafach (1000-1000000).",
|
||||
"settings.security.captchaKey": "Allwedd Safle hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Ewch i www.hcaptcha.com i gael yr allwedd a'r hymwerydd.",
|
||||
"settings.security.captchaSecret": "Cyfrinach Safle hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "E-bost derbynnydd",
|
||||
"settings.title": "Gosodiadau",
|
||||
"settings.updateAvailable": "Mae diweddariad {version} newydd ar gael.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Uwch",
|
||||
"subscribers.advancedQueryHelp": "Mynegiad SQL rhannol i wneud ymholiad ynghylch priodoleddau tanysgrifiwr",
|
||||
"subscribers.attribs": "Priodoleddau",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copiwch y token mynediad API nawr. Ni chaiff ei ddangos eto.",
|
||||
"users.cantDeleteRole": "Ni all dileu rôl sy'n cael ei defnyddio.",
|
||||
"users.firstTime": "Dyma osodiad ffres. Dewiswch enw defnyddiwr a chyfrinair ar gyfer cyfrif Yr Uwch Weinydd.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Mewngofnodi neu gyfrinair annilys",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Cais dilys annilys",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Mewngofnodi diwethaf",
|
||||
"users.listPerms": "Caniatadau rhestru",
|
||||
"users.listPermsWarning": "mae caniatadau lists:get_all neu lists:manage_all wedi'u galluogi sy'n gor-wysgo caniatadau'r rhestr.",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Allgofnodi",
|
||||
"users.needSuper": "Ni ellir diweddaru Defnyddiwr(iaid). Rhaid i un Ddefnyddiwr Super Weinydd bod ar waith o leiaf.",
|
||||
"users.newListRole": "Rôl rhestr newydd",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Defnyddiwr newydd",
|
||||
"users.newUserRole": "Rôl defnyddiwr newydd",
|
||||
"users.password": "Cyfrinair",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Ailadrodd y cyfrinair",
|
||||
"users.perms": "Caniatadau",
|
||||
"users.profile": "Proffil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rôl | Rolau",
|
||||
"users.roleGroup": "Grŵp",
|
||||
"users.roles": "Rolau",
|
||||
"users.status.disabled": "Analluog",
|
||||
"users.status.enabled": "Galluog",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Math",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Uwch Weinydd",
|
||||
|
||||
34
i18n/da.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Forældreløse",
|
||||
"email.data.info": "En kopi af alle data, der er registreret på dig, vedhæftes som en fil i JSON-format. Det kan ses i en teksteditor.",
|
||||
"email.data.title": "Dine data",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Bekræft abonnement",
|
||||
"email.optin.confirmSubHelp": "Bekræft dit abonnement ved at klikke på nedenstående knap.",
|
||||
"email.optin.confirmSubInfo": "Du er blevet føjet til følgende lister:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopier",
|
||||
"globals.buttons.delete": "Slet",
|
||||
"globals.buttons.deleteAll": "Slet alle",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Redigere",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Aktiveret",
|
||||
"globals.buttons.insert": "Indsætte",
|
||||
"globals.buttons.learnMore": "Lær mere",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Ugyldige felter: {name}",
|
||||
"globals.messages.invalidID": "Ugyldige ID'er",
|
||||
"globals.messages.invalidUUID": "Ugyldig(e) UUID(s)",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Manglende felt(er): {name}",
|
||||
"globals.messages.notFound": "{name} ikke fundet",
|
||||
"globals.messages.numSelected": "{num} valgt",
|
||||
"globals.messages.passwordChange": "Indtast en værdi, der skal ændres",
|
||||
"globals.messages.passwordChangeFull": "Ryd og indtast den fulde adgangskode igen i '{name}'.",
|
||||
"globals.messages.permissionDenied": "Adgang nægtet: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Skabelon | Skabeloner",
|
||||
"globals.terms.templates": "Skabeloner",
|
||||
"globals.terms.tx": "Transaktionel | Transaktionel",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Bruger | Brugere",
|
||||
"globals.terms.users": "Brugere",
|
||||
"globals.terms.year": "År | År",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Ikke-understøttet filtype ({type})",
|
||||
"media.upload": "Upload",
|
||||
"media.uploadHelp": "Klik eller træk et eller flere billeder hertil",
|
||||
"media.uploadImage": "Upload billede",
|
||||
"menu.allCampaigns": "Alle kampagner",
|
||||
"menu.allLists": "Alle lister",
|
||||
"menu.allSubscribers": "Alle abonnenter",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Optag opt-in IP-adresse",
|
||||
"settings.privacy.recordOptinIPHelp": "Optag IP-adressen for dobbelt opt-ins i abonnentattributter.",
|
||||
"settings.restart": "Genstart",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Opret automatisk brugere",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Opret automatisk bruger ved første login, hvis kontoen ikke eksisterer.",
|
||||
"settings.security.OIDCClientID": "Klient-ID",
|
||||
"settings.security.OIDCClientSecret": "Klient-hemmelighed",
|
||||
"settings.security.OIDCDefaultListRole": "Standard liste rolle",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Standardrolle tildelt brugere, der automatisk oprettes fra OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Standard brugerrolle",
|
||||
"settings.security.OIDCHelp": "Aktivér OpenID Connect OAuth2-login via en OAuth-udbyder.",
|
||||
"settings.security.OIDCName": "Provider name",
|
||||
"settings.security.OIDCRedirectURL": "Redirect-URL til OAuth-udbyder",
|
||||
"settings.security.OIDCRedirectWarning": "Dette ser ikke ud til at være en produktions-URL. Skift 'Root URL' i 'Generelt' indstillinger.",
|
||||
"settings.security.OIDCURL": "Udbyder-URL",
|
||||
"settings.security.OIDCWarning": "Når OIDC er aktiveret, deaktiveres standard adgang med adgangskode. Forkert konfiguration kan låse dig ude.",
|
||||
"settings.security.altchaComplexity": "Altcha kompleksitet",
|
||||
"settings.security.altchaComplexityHelp": "Højere værdier giver bedre sikkerhed, men langsommere løsning (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com SiteKey",
|
||||
"settings.security.captchaKeyHelp": "Besøg www.hcaptcha.com for at få nøglen og hemmeligheden.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com hemmelighed",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "For at e-maile",
|
||||
"settings.title": "Indstillinger",
|
||||
"settings.updateAvailable": "En ny opdatering {version} er tilgængelig.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avanceret",
|
||||
"subscribers.advancedQueryHelp": "Delvist SQL-udtryk til forespørgsel på abonnentattributter",
|
||||
"subscribers.attribs": "Attributter",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Kopier API-adgangstokenen nu. Den vil ikke blive vist igen.",
|
||||
"users.cantDeleteRole": "Kan ikke slette en rolle, der er i brug.",
|
||||
"users.firstTime": "Dette er en ny installation. Vælg et brugernavn og adgangskode til Super Admin-kontoen.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Ugyldig login eller adgangskode",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Ugyldig godkendelsesanmodning",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Sidste login",
|
||||
"users.listPerms": "Listetilladelser",
|
||||
"users.listPermsWarning": "lists:get_all eller lists:manage_all er aktiveret og overskriver per-listetilladelser",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Log ud",
|
||||
"users.needSuper": "Bruger(e) kunne ikke opdateres. Der skal være mindst én aktiv Super Admin-bruger.",
|
||||
"users.newListRole": "Ny liste rolle",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Ny bruger",
|
||||
"users.newUserRole": "Ny bruger rolle",
|
||||
"users.password": "Adgangskode",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Gentag adgangskode",
|
||||
"users.perms": "Tilladelser",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rolle | Roller",
|
||||
"users.roleGroup": "Gruppe",
|
||||
"users.roles": "Roller",
|
||||
"users.status.disabled": "Deaktiveret",
|
||||
"users.status.enabled": "Aktiveret",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Type",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/de.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Verwaiste",
|
||||
"email.data.info": "Eine Kopie aller gespeicherten Daten ist in der angehängten JSON-Datei gespeichert. Sie kann in einem Texteditor angezeigt werden.",
|
||||
"email.data.title": "Deine Daten",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Abonnement bestätigen",
|
||||
"email.optin.confirmSubHelp": "Bestätige dein Abonnement mit einem Klick auf den nachfolgenden Button.",
|
||||
"email.optin.confirmSubInfo": "Du hast dich für folgende Listen angemeldet:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopieren",
|
||||
"globals.buttons.delete": "Löschen",
|
||||
"globals.buttons.deleteAll": "Alle Löschen",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Bearbeiten",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Aktiviert",
|
||||
"globals.buttons.insert": "Einfügen",
|
||||
"globals.buttons.learnMore": "Erfahre mehr",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Ungültige Felder: {name}",
|
||||
"globals.messages.invalidID": "Ungültige ID",
|
||||
"globals.messages.invalidUUID": "Ungültige UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Fehlende Felder: {name}",
|
||||
"globals.messages.notFound": "{name} nicht gefunden",
|
||||
"globals.messages.numSelected": "{num} ausgewählt",
|
||||
"globals.messages.passwordChange": "Gib dein Passwort für die Änderung ein",
|
||||
"globals.messages.passwordChangeFull": "Löschen und das vollständige Passwort in '{name}' erneut eingeben.",
|
||||
"globals.messages.permissionDenied": "Zugriff verweigert: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Vorlage | Vorlagen",
|
||||
"globals.terms.templates": "Vorlagen",
|
||||
"globals.terms.tx": "Transaktion | Transaktionen",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Benutzer | Benutzer",
|
||||
"globals.terms.users": "Benutzer",
|
||||
"globals.terms.year": "Jahr | Jahre",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Nicht unterstützter Dateityp ({type})",
|
||||
"media.upload": "Hochladen",
|
||||
"media.uploadHelp": "Klicke oder ziehe ein oder mehrere Bilder hierhin",
|
||||
"media.uploadImage": "Bilder Hochladen",
|
||||
"menu.allCampaigns": "Alle Kampagnen",
|
||||
"menu.allLists": "Alle Listen",
|
||||
"menu.allSubscribers": "Alle Abonnenten",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Opt-in-IP-Adresse protokollieren",
|
||||
"settings.privacy.recordOptinIPHelp": "Protokollieren Sie die IP-Adresse der doppelten Einwilligung in den Abonnentenattributen.",
|
||||
"settings.restart": "Neustarten",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Benutzer automatisch erstellen",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Benutzer beim ersten Login automatisch erstellen, wenn das Konto nicht existiert.",
|
||||
"settings.security.OIDCClientID": "Client-ID",
|
||||
"settings.security.OIDCClientSecret": "Client Secret",
|
||||
"settings.security.OIDCDefaultListRole": "Standardlistenrolle",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Standardrolle, die Benutzern zugewiesen wird, die automatisch über OIDC erstellt wurden.",
|
||||
"settings.security.OIDCDefaultUserRole": "Standardbenutzerrolle",
|
||||
"settings.security.OIDCHelp": "Aktivieren Sie die Anmeldung über OpenID Connect OAuth2 über einen OAuth-Anbieter.",
|
||||
"settings.security.OIDCName": "Anbietername",
|
||||
"settings.security.OIDCRedirectURL": "Redirect-URL für oAuth-Anbieter",
|
||||
"settings.security.OIDCRedirectWarning": "Dies scheint keine Produktions-URL zu sein. Ändern Sie die Stamm-URL in den 'Allgemeinen' Einstellungen.",
|
||||
"settings.security.OIDCURL": "Provider-URL",
|
||||
"settings.security.OIDCWarning": "Wenn OIDC aktiviert ist, ist die Standard-Anmeldung mit Passwort deaktiviert. Eine ungültige Konfiguration kann dazu führen, dass Sie ausgesperrt werden.",
|
||||
"settings.security.altchaComplexity": "Altcha-Komplexität",
|
||||
"settings.security.altchaComplexityHelp": "Höhere Werte bieten bessere Sicherheit, aber langsamere Lösung (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com SiteKey",
|
||||
"settings.security.captchaKeyHelp": "Besuchen Sie www.hcaptcha.com, um den Schlüssel und das Geheimnis zu erhalten.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com Geheimnis",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Empfänger E-Mail",
|
||||
"settings.title": "Einstellungen",
|
||||
"settings.updateAvailable": "Ein neues Update auf {version} ist verfügbar.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Erweitert",
|
||||
"subscribers.advancedQueryHelp": "Partieller SQL Ausdruck um Attribute der Abonnenten abzufragen",
|
||||
"subscribers.attribs": "Attribute",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Kopieren Sie jetzt den API-Zugriffstoken. Er wird nicht erneut angezeigt.",
|
||||
"users.cantDeleteRole": "Rolle kann nicht gelöscht werden, da sie verwendet wird.",
|
||||
"users.firstTime": "Dies ist eine neue Installation. Wählen Sie einen Benutzernamen und ein Passwort für das Super Admin-Konto.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Ungültige Anmeldung oder falsches Passwort",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Ungültige Auth-Anforderung",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Letzte Anmeldung",
|
||||
"users.listPerms": "Berechtigungen für Listen",
|
||||
"users.listPermsWarning": "lists:get_all oder lists:manage_all sind aktiviert und außer Kraft gesetzt die Berechtigungen pro Liste.",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Abmelden",
|
||||
"users.needSuper": "Der/die Benutzer konnte(n) nicht aktualisiert werden. Es muss mindestens ein aktiver Super Admin-Benutzer vorhanden sein.",
|
||||
"users.newListRole": "Neue Listenrolle",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Neuer Benutzer",
|
||||
"users.newUserRole": "Neue Benutzerrolle",
|
||||
"users.password": "Passwort",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Passwort wiederholen",
|
||||
"users.perms": "Berechtigungen",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rolle | Rollen",
|
||||
"users.roleGroup": "Gruppe",
|
||||
"users.roles": "Rollen",
|
||||
"users.status.disabled": "Deaktiviert",
|
||||
"users.status.enabled": "Aktiviert",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Typ",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/el.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "\"Ορφανοί\" συνδρομητές",
|
||||
"email.data.info": "Ένα αντίγραφο όλων των δεδομένων που έχουν καταγραφεί για εσάς είναι συνημμένο ως αρχείο σε μορφή JSON. Μπορεί να προβληθεί με έναν επεξεργαστή κειμένου.",
|
||||
"email.data.title": "Τα δεδομένα σας",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Επιβεβαίωση συνδρομής",
|
||||
"email.optin.confirmSubHelp": "Επιβεβαιώστε την εγγραφή σας κάνοντας κλικ στο κουμπί παρακάτω.",
|
||||
"email.optin.confirmSubInfo": "Έχετε προστεθεί στις παρακάτω λίστες:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Αντιγραφή",
|
||||
"globals.buttons.delete": "Διαγραφή",
|
||||
"globals.buttons.deleteAll": "Διαγραφή όλων",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Επεξεργασία",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Ενεργοποιημένο",
|
||||
"globals.buttons.insert": "Εισαγωγή",
|
||||
"globals.buttons.learnMore": "Μάθετε περισσότερα",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Μη έγκυρα πεδία: {name}",
|
||||
"globals.messages.invalidID": "Μυ έγκυρο/-α ID",
|
||||
"globals.messages.invalidUUID": "Μυ έγκυρο/-α UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Λείπουν πεδία: {name}",
|
||||
"globals.messages.notFound": "Το {name} δεν βρέθηκε",
|
||||
"globals.messages.numSelected": "{num} επιλεγμένα",
|
||||
"globals.messages.passwordChange": "Εισάγετε νέο περιεχόμενο για αλλαγή",
|
||||
"globals.messages.passwordChangeFull": "Εκκαθάριση και επανεισαγωγή του συνθηματικού στο '{name}'.",
|
||||
"globals.messages.permissionDenied": "Δεν επιτρέπεται η πρόσβαση: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Προσχέδιο | Προσχέδια",
|
||||
"globals.terms.templates": "Προσχέδια",
|
||||
"globals.terms.tx": "Συναλλακτική | Συναλλακτικές",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Χρήστης | Χρήστες",
|
||||
"globals.terms.users": "Χρήστες",
|
||||
"globals.terms.year": "Έτος | Έτη",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Μη υποστηριζόμενος τύπος αρχείου ({type})",
|
||||
"media.upload": "Μεταφόρτωση",
|
||||
"media.uploadHelp": "Κάντε κλικ ή σύρετε μία ή περισσότερες εικόνες εδώ",
|
||||
"media.uploadImage": "Μεταφόρτωση εικόνας",
|
||||
"menu.allCampaigns": "Όλες οι εκστρατείες",
|
||||
"menu.allLists": "Όλες οι λίστες",
|
||||
"menu.allSubscribers": "Όλοι οι συνδρομητές",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Καταγραφή διεύθυνσης IP με τη συγκατάθεση",
|
||||
"settings.privacy.recordOptinIPHelp": "Καταγράψτε τη διεύθυνση IP της διπλής συγκατάθεσης στα χαρακτηριστικά των συνδρομητών.",
|
||||
"settings.restart": "Επανεκίννηση",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Αυτόματη δημιουργία χρηστών",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Δημιουργεί αυτόματα χρήστη κατά την πρώτη σύνδεση αν ο λογαριασμός δεν υπάρχει.",
|
||||
"settings.security.OIDCClientID": "Ταυτοποίηση πελάτη",
|
||||
"settings.security.OIDCClientSecret": "Μυστικό πελάτη",
|
||||
"settings.security.OIDCDefaultListRole": "Προεπιλεγμένος ρόλος λίστας",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Προεπιλεγμένος ρόλος που ανατίθεται στους χρήστες που δημιουργούνται αυτόματα από OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Προεπιλεγμένος ρόλος χρήστη",
|
||||
"settings.security.OIDCHelp": "Ενεργοποίηση σύνδεσης OAuth2 OpenID Connect μέσω ενός παροχέα OAuth.",
|
||||
"settings.security.OIDCName": "Όνομα παρόχου",
|
||||
"settings.security.OIDCRedirectURL": "URL ανακατεύθυνσης για τον πάροχο oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Αυτό δεν φαίνεται να είναι ένα URL παραγωγής. Αλλάξτε το URL ριζικού στο 'Γενικές' ρυθμίσεις.",
|
||||
"settings.security.OIDCURL": "URL παρόχου",
|
||||
"settings.security.OIDCWarning": "Όταν είναι ενεργοποιημένο το OIDC, η προεπιλεγμένη σύνδεση μέσω κωδικού πρόσβασης απενεργοποιείται. Μη έγκυρη ρύθμιση μπορεί να σας αποκλείσει.",
|
||||
"settings.security.altchaComplexity": "Πολυπλοκότητα Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Μεγαλύτερες τιμές παρέχουν καλύτερη ασφάλεια, αλλά επιβραδύνουν τη λύση (1000-1000000).",
|
||||
"settings.security.captchaKey": "SiteKey του hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Επισκεφθείτε το www.hcaptcha.com για να λάβετε το κλειδί και το μυστικό.",
|
||||
"settings.security.captchaSecret": "Μυστικό (secret) του hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Στο e-mail",
|
||||
"settings.title": "Ρυθμίσεις",
|
||||
"settings.updateAvailable": "Μια νέα ενημέρωση {version} είναι διαθέσιμη.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Για προχωρημένους",
|
||||
"subscribers.advancedQueryHelp": "Μερική έκφραση SQL για την αναζήτηση χαρακτηριστικών συνδρομητών",
|
||||
"subscribers.attribs": "Χαρακτηριστικά",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Αντιγράψτε το τυχαίο κλειδί πρόσβασης στο API τώρα. Δεν θα εμφανιστεί ξανά.",
|
||||
"users.cantDeleteRole": "Δεν είναι δυνατή η διαγραφή του ρόλου που χρησιμοποιείται.",
|
||||
"users.firstTime": "Αυτή είναι μια καινούργια εγκατάσταση. Επιλέξτε ένα όνομα χρήστη και έναν κωδικό πρόσβασης για τον υπερδιαχειριστή του συστήματος.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Μη έγκυρη σύνδεση ή κωδικός πρόσβασης",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Μη έγκυρο αίτημα εξουσιοδότησης",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Τελευταία σύνδεση",
|
||||
"users.listPerms": "Αρμοδιότητες λίστας",
|
||||
"users.listPermsWarning": "ενεργοποιήθηκε λειτουργία για lists:get_all ή lists:manage_all που αντικαθιστά τις αρμοδιότητες ανά λίστα",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Αποσύνδεση",
|
||||
"users.needSuper": "Δεν ήταν δυνατή η ενημέρωση χρήστη(ών). Πρέπει να υπάρχει τουλάχιστον ένας ενεργός χρήστης ως υπερδιαχειριστής.",
|
||||
"users.newListRole": "Νέος ρόλος λίστας",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Νέος χρήστης",
|
||||
"users.newUserRole": "Νέος ρόλος χρήστη",
|
||||
"users.password": "Κωδικός πρόσβασης",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Επανάληψη κωδικού πρόσβασης",
|
||||
"users.perms": "Αρμοδιότητες",
|
||||
"users.profile": "Προφίλ",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Ρόλος | Ρόλοι",
|
||||
"users.roleGroup": "Ομάδα",
|
||||
"users.roles": "Ρόλοι",
|
||||
"users.status.disabled": "Απενεργοποιημένο",
|
||||
"users.status.enabled": "Ενεργοποιημένο",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Τύπος",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Υπερδιαχειριστής",
|
||||
|
||||
36
i18n/en.json
@@ -142,6 +142,8 @@
|
||||
"globals.buttons.clone": "Clone",
|
||||
"globals.buttons.close": "Close",
|
||||
"globals.buttons.continue": "Continue",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.copy": "Copy",
|
||||
"globals.buttons.delete": "Delete",
|
||||
"globals.buttons.deleteAll": "Delete all",
|
||||
@@ -174,6 +176,7 @@
|
||||
"globals.fields.type": "Type",
|
||||
"globals.fields.updatedAt": "Updated",
|
||||
"globals.fields.uuid": "UUID",
|
||||
"globals.messages.numSelected": "{num} selected",
|
||||
"globals.messages.confirm": "Are you sure?",
|
||||
"globals.messages.confirmDiscard": "Discard changes?",
|
||||
"globals.messages.copied": "Copied",
|
||||
@@ -190,6 +193,7 @@
|
||||
"globals.messages.errorUpdating": "Error updating {name}: {error}",
|
||||
"globals.messages.internalError": "Internal server error",
|
||||
"globals.messages.invalidData": "Invalid data",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.invalidFields": "Invalid fields: {name}",
|
||||
"globals.messages.invalidID": "Invalid ID(s)",
|
||||
"globals.messages.invalidUUID": "Invalid UUID(s)",
|
||||
@@ -245,6 +249,7 @@
|
||||
"globals.terms.users": "Users",
|
||||
"globals.terms.year": "Year | Years",
|
||||
"globals.terms.import": "Import",
|
||||
"globals.terms.url": "URL",
|
||||
"import.alreadyRunning": "An import is already running. Wait for it to finish or stop it before trying again.",
|
||||
"import.blocklist": "Blocklist",
|
||||
"import.csvDelim": "CSV delimiter",
|
||||
@@ -305,7 +310,6 @@
|
||||
"media.unsupportedFileType": "Unsupported file type ({type})",
|
||||
"media.upload": "Upload",
|
||||
"media.uploadHelp": "Click or drag one or more images here",
|
||||
"media.uploadImage": "Upload image",
|
||||
"menu.allCampaigns": "All campaigns",
|
||||
"menu.allLists": "All lists",
|
||||
"menu.allSubscribers": "All subscribers",
|
||||
@@ -531,6 +535,13 @@
|
||||
"settings.security.OIDCURL": "Provider URL",
|
||||
"settings.security.OIDCName": "Provider name",
|
||||
"settings.security.OIDCWarning": "When OIDC is enabled, default password login is disabled. Invalid config can lock you out.",
|
||||
"settings.security.OIDCAutoCreateUsers": "Auto-create users",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Automatically create user on first login if the account doesn't exist.",
|
||||
"settings.security.OIDCDefaultUserRole": "Default user role",
|
||||
"settings.security.OIDCDefaultListRole": "Default list role",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Default role assigned to users auto-created from OIDC.",
|
||||
"settings.security.altchaComplexity": "Altcha Complexity",
|
||||
"settings.security.altchaComplexityHelp": "Higher values provide better security but slower solving (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com SiteKey",
|
||||
"settings.security.captchaKeyHelp": "Visit www.hcaptcha.com to obtain the key and secret.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com secret",
|
||||
@@ -599,6 +610,7 @@
|
||||
"subscribers.status.unconfirmed": "Unconfirmed",
|
||||
"subscribers.status.unsubscribed": "Unsubscribed",
|
||||
"subscribers.subscribersDeleted": "{num} subscriber(s) deleted",
|
||||
"subscribers.activity": "Activity",
|
||||
"templates.cantDeleteDefault": "Cannot delete non-existent or default template",
|
||||
"templates.default": "Default",
|
||||
"templates.dummyName": "Dummy campaign",
|
||||
@@ -650,5 +662,25 @@
|
||||
"users.userRole": "User role | User roles",
|
||||
"users.userRoles": "User roles",
|
||||
"users.username": "Username",
|
||||
"users.usernameHelp": "Used with password login"
|
||||
"users.usernameHelp": "Used with password login",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.newPassword": "New password",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.invalidPassword": "Invalid password"
|
||||
}
|
||||
|
||||
34
i18n/eo.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Orfes",
|
||||
"email.data.info": "S'adjunta una còpia de totes les dades enregistrades sobre la teva persona en un fitxer en format JSON. Es pot veure en un editor de text.",
|
||||
"email.data.title": "Les teves dades ",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirma la subscripció",
|
||||
"email.optin.confirmSubHelp": "Confirmeu la terva subscripció fent clic al botó següent.",
|
||||
"email.optin.confirmSubInfo": "Heu estat afegit a les llistes següents:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copiar",
|
||||
"globals.buttons.delete": "Esborra",
|
||||
"globals.buttons.deleteAll": "Esborra tot",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Edita",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Habilitat",
|
||||
"globals.buttons.insert": "Inserta",
|
||||
"globals.buttons.learnMore": "Saber-nes més",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Camps no vàlids: {name}",
|
||||
"globals.messages.invalidID": "ID(s) no vàlid",
|
||||
"globals.messages.invalidUUID": "UUID(s) no vàlid",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Falten camps: {name}",
|
||||
"globals.messages.notFound": "No s'ha trobat {name} ",
|
||||
"globals.messages.numSelected": "{num} elektitaj",
|
||||
"globals.messages.passwordChange": "Introduïu un valor per canviar",
|
||||
"globals.messages.passwordChangeFull": "Buida i torna a introduir la contrasenya completa a '{name}'.",
|
||||
"globals.messages.permissionDenied": "Permeso rifuzita: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Plantilla | Plantilles",
|
||||
"globals.terms.templates": "Plantilles",
|
||||
"globals.terms.tx": "Transaccional | Transaccionals",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Uzanto | Uzantoj",
|
||||
"globals.terms.users": "Uzantoj",
|
||||
"globals.terms.year": "Any | Anys",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "El tipus de fitxer ({type}) no és compatible",
|
||||
"media.upload": "Carrega",
|
||||
"media.uploadHelp": "Fes clic o arrossega una o més imatges aquí",
|
||||
"media.uploadImage": "Carrega la imatge",
|
||||
"menu.allCampaigns": "Totes les campanyes",
|
||||
"menu.allLists": "Totes les llistes",
|
||||
"menu.allSubscribers": "Tots els subscriptors",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Registra l'adreça IP de l'opt-in",
|
||||
"settings.privacy.recordOptinIPHelp": "Registra l'adreça IP dels opt-ins dobles en els atributs del subscrit.",
|
||||
"settings.restart": "Reinicia",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Aŭtomate krei uzantojn",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Aŭtomate krei uzanton ĉe la unua ensaluto se la konto ne ekzistas.",
|
||||
"settings.security.OIDCClientID": "Klient-ID",
|
||||
"settings.security.OIDCClientSecret": "Klient-sekreto",
|
||||
"settings.security.OIDCDefaultListRole": "Defaŭlta listo-rolo",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Defaŭlta rolo asignita al uzantoj aŭtomate kreitaj per OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Defaŭlta uzant-rolo",
|
||||
"settings.security.OIDCHelp": "Ebligi OpeID Connect OAuth2 ensaluton per OAuth provizanto.",
|
||||
"settings.security.OIDCName": "Nomo de provizanto",
|
||||
"settings.security.OIDCRedirectURL": "URL for redirektado de oAuth provizanto",
|
||||
"settings.security.OIDCRedirectWarning": "Ĉi tiu ŝajnas esti ne produktadata URL. Ŝanĝu la Radika URL en 'Ĝenerala' agordoj.",
|
||||
"settings.security.OIDCURL": "Provizanto-URL",
|
||||
"settings.security.OIDCWarning": "Se OIDC estas ebligita, la defaŭlta ensaluto per pasvorto malŝaltiĝas. Nevalida agordo povas bloki vin eksteren.",
|
||||
"settings.security.altchaComplexity": "Altcha Kompleksaĵo",
|
||||
"settings.security.altchaComplexityHelp": "Pli altaj valoroj provizas pli bonan sekurecon, sed pli malrapidajn solvokapablojn (1000-1000000).",
|
||||
"settings.security.captchaKey": "Clau del lloc hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Visiteu www.hcaptcha.com per obtenir la clau i el secret.",
|
||||
"settings.security.captchaSecret": "Secret del lloc hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Destinatari del correu electrònic",
|
||||
"settings.title": "Configuració",
|
||||
"settings.updateAvailable": "Hi ha disponible una nova actualització {versió}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avançat",
|
||||
"subscribers.advancedQueryHelp": "Expressió SQL parcial per consultar els atributs del subscriptor",
|
||||
"subscribers.attribs": "Atributs",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Kopiu la API-alirajton nun. Ĝi ne estos montrata denove.",
|
||||
"users.cantDeleteRole": "Ne povas forigi rolon, kiu estas uzata.",
|
||||
"users.firstTime": "Ĉi tio estas nova instalo. Elektu uzantonomon kaj pasvorton por la Super Admin-konto.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Nevalida ensaluto aŭ pasvorto",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Nevalida aŭtentiga peto",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Lasta ensaluto",
|
||||
"users.listPerms": "Listrajtoj",
|
||||
"users.listPermsWarning": "lists:get_all aŭ lists:manage_all estas ebligitaj, ĉar ili anstataŭigas rajtojn laŭ listo",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Tanca sessió",
|
||||
"users.needSuper": "Tiom da uzanto(j) maltajperis. Estu almenaŭ unu aktiva Super Admin uzanto.",
|
||||
"users.newListRole": "Nova listrolo",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nova uzanto",
|
||||
"users.newUserRole": "Nova uzantrolo",
|
||||
"users.password": "Pasvorto",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Repetu la pasvorton",
|
||||
"users.perms": "Rajtoj",
|
||||
"users.profile": "Profilo",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rolo | Roloj",
|
||||
"users.roleGroup": "Grupo",
|
||||
"users.roles": "Roloj",
|
||||
"users.status.disabled": "Malaktiva",
|
||||
"users.status.enabled": "Aktiva",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tipo",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Supa Admin",
|
||||
|
||||
34
i18n/es.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Huérfanos",
|
||||
"email.data.info": "Una copia de todos sus datos recopilados está adjunta en un archivo de formato JSON. Puede ser visto en un editor de textos.",
|
||||
"email.data.title": "Sus datos",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirmar la suscripción",
|
||||
"email.optin.confirmSubHelp": "Para confirmar su suscripción debe hacer clic en el siguiente botón.",
|
||||
"email.optin.confirmSubInfo": "Su correo electrónico ha sido agregado a las siguientes listas:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copiar",
|
||||
"globals.buttons.delete": "Eliminar",
|
||||
"globals.buttons.deleteAll": "Eliminar todos",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Editar",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Habilitar",
|
||||
"globals.buttons.insert": "Insertar",
|
||||
"globals.buttons.learnMore": "Conocer más",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Campos inválidos: {name}",
|
||||
"globals.messages.invalidID": "ID inválido",
|
||||
"globals.messages.invalidUUID": "UUID inválido",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Falta el campo(s): {name}",
|
||||
"globals.messages.notFound": "{name} no encontrado",
|
||||
"globals.messages.numSelected": "{num} seleccionados",
|
||||
"globals.messages.passwordChange": "Ingresar una contraseña para cambiar",
|
||||
"globals.messages.passwordChangeFull": "Borre y vuelva a ingresar la contraseña completa en '{name}'.",
|
||||
"globals.messages.permissionDenied": "Permiso denegado: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Plantilla | Plantillas",
|
||||
"globals.terms.templates": "Plantillas",
|
||||
"globals.terms.tx": "Transaccional | Transaccional",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Usuario | Usuarios",
|
||||
"globals.terms.users": "Usuarios",
|
||||
"globals.terms.year": "Año | Años",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Tipo de archivo no soportado ({type})",
|
||||
"media.upload": "Cargar",
|
||||
"media.uploadHelp": "Seleccione o arrastre una o más imágenes aquí",
|
||||
"media.uploadImage": "Cargar imagen",
|
||||
"menu.allCampaigns": "Todas las campañas",
|
||||
"menu.allLists": "Todas las listas",
|
||||
"menu.allSubscribers": "Todos los suscriptores",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Grabar dirección IP de inscripción",
|
||||
"settings.privacy.recordOptinIPHelp": "Registrar la dirección IP de doble inscripción en los atributos del suscriptor.",
|
||||
"settings.restart": "Reiniciar",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Creación automática de usuarios",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Crear automáticamente un usuario en el primer inicio de sesión si la cuenta no existe.",
|
||||
"settings.security.OIDCClientID": "ID del cliente",
|
||||
"settings.security.OIDCClientSecret": "Secreto del cliente",
|
||||
"settings.security.OIDCDefaultListRole": "Rol predeterminado en la lista",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Rol predeterminado asignado a los usuarios creados automáticamente desde OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Rol de usuario predeterminado",
|
||||
"settings.security.OIDCHelp": "Habilita el inicio de sesión OAuth2 de OpenID Connect mediante un proveedor de OAuth.",
|
||||
"settings.security.OIDCName": "Nombre del proveedor",
|
||||
"settings.security.OIDCRedirectURL": "URL de redirección para el proveedor de OAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Esto no parece ser una URL de producción. Cambie la URL raíz en la configuración 'General'.",
|
||||
"settings.security.OIDCURL": "URL del proveedor",
|
||||
"settings.security.OIDCWarning": "Cuando se habilita OIDC, el inicio de sesión con contraseña predeterminada se deshabilita. Una configuración incorrecta puede bloquearlo.",
|
||||
"settings.security.altchaComplexity": "Complejidad Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Valores más altos ofrecen mejor seguridad pero una resolución más lenta (1000-1000000).",
|
||||
"settings.security.captchaKey": "Clave de sitio hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Visite www.hcaptcha.com para conseguir la SiteKey y el secret.",
|
||||
"settings.security.captchaSecret": "Secreto hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Correo electrónico del destinatario",
|
||||
"settings.title": "Configuraciones",
|
||||
"settings.updateAvailable": "Una actualización a la {version} está disponible.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avanzado",
|
||||
"subscribers.advancedQueryHelp": "Expresión SQL parcial para consultar los atributos de un suscriptor",
|
||||
"subscribers.attribs": "Atributos",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copia el token de acceso a la API ahora. No se mostrará de nuevo.",
|
||||
"users.cantDeleteRole": "No se puede eliminar la función que está en uso.",
|
||||
"users.firstTime": "Esta es una instalación nueva. Elija un nombre de usuario y una contraseña para la cuenta de superadmin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Inicio de sesión o contraseña no válidos",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Solicitud de autenticación no válida",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Último inicio de sesión",
|
||||
"users.listPerms": "Permisos de lista",
|
||||
"users.listPermsWarning": "Se han habilitado los permisos lists:get_all o lists:manage_all, lo que anula los permisos individuales de la lista",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Salir",
|
||||
"users.needSuper": "No se pueden actualizar los usuarios. Tiene que haber al menos un usuario Super Admin activo.",
|
||||
"users.newListRole": "Nuevo rol de lista",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nuevo usuario",
|
||||
"users.newUserRole": "Nuevo rol de usuario",
|
||||
"users.password": "Contraseña",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Repetir contraseña",
|
||||
"users.perms": "Permisos",
|
||||
"users.profile": "Perfil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rol | Roles",
|
||||
"users.roleGroup": "Grupo",
|
||||
"users.roles": "Roles",
|
||||
"users.status.disabled": "Desactivado",
|
||||
"users.status.enabled": "Habilitado",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tipo",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/fi.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Orvot",
|
||||
"email.data.info": "Kopio kaikista sinusta tallennetuista tiedoista on liitetiedostona JSON-muodossa. Voit tarkastella tiedostoa tekstieditorissa.",
|
||||
"email.data.title": "Sinun tietosi",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Vahvista postituslistalle liittyminen",
|
||||
"email.optin.confirmSubHelp": "Vahvista postituslistalle liittyminen napsauttamalla alla olevaa painiketta.",
|
||||
"email.optin.confirmSubInfo": "Sinut on lisätty seuraaville listoille:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopioi",
|
||||
"globals.buttons.delete": "Poista",
|
||||
"globals.buttons.deleteAll": "Poista kaikki",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Muokkaa",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Käytössä",
|
||||
"globals.buttons.insert": "Lisää",
|
||||
"globals.buttons.learnMore": "Lue lisää",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Virheelliset kentät: {name}",
|
||||
"globals.messages.invalidID": "Virheelliset ID:t",
|
||||
"globals.messages.invalidUUID": "Virheelliset UUID:t",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Puuttuvat kentät: {name}",
|
||||
"globals.messages.notFound": "{name} ei löytynyt",
|
||||
"globals.messages.numSelected": "{num} valittuna",
|
||||
"globals.messages.passwordChange": "Syötä muuttaaksesi arvoa",
|
||||
"globals.messages.passwordChangeFull": "Tyhjennä ja kirjoita uudelleen täysi salasana kohdassa '{name}'.",
|
||||
"globals.messages.permissionDenied": "Pääsy evätty: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Mallipohja | Mallipohjat",
|
||||
"globals.terms.templates": "Mallipohja",
|
||||
"globals.terms.tx": "Transaktiivinen | Transaktiiviset",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Käyttäjä | Käyttäjät",
|
||||
"globals.terms.users": "Käyttäjät",
|
||||
"globals.terms.year": "Vuosi | Vuodet",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Tiedostotyyppiä ei tueta ({type})",
|
||||
"media.upload": "Lataa",
|
||||
"media.uploadHelp": "Klikkaa tai raahaa tähän yksi tai useampi kuva",
|
||||
"media.uploadImage": "Lataa kuva",
|
||||
"menu.allCampaigns": "Kaikki kampanjat",
|
||||
"menu.allLists": "Kaikki listat",
|
||||
"menu.allSubscribers": "Kaikki tilaajat",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Kirjaa tilauksen IP-osoite",
|
||||
"settings.privacy.recordOptinIPHelp": "Kirjaa varmennetun tilaajan IP-osoite tilaajan attribuutteihin.",
|
||||
"settings.restart": "Käynnistä uudelleen",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Luo käyttäjät automaattisesti",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Luo käyttäjä automaattisesti ensimmäisellä kirjautumiskerralla, jos tiliä ei ole olemassa.",
|
||||
"settings.security.OIDCClientID": "Asiakas-ID",
|
||||
"settings.security.OIDCClientSecret": "Asiakasavain",
|
||||
"settings.security.OIDCDefaultListRole": "Oletuslistan rooli",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Oletusrooli, joka annetaan OIDC:stä automaattisesti luoduille käyttäjille.",
|
||||
"settings.security.OIDCDefaultUserRole": "Oletuskäyttäjän rooli",
|
||||
"settings.security.OIDCHelp": "Salli OpenID Connect OAuth2 -sisäänkirjautuminen OAuth-toimittajan kautta.",
|
||||
"settings.security.OIDCName": "Tarjoajan nimi",
|
||||
"settings.security.OIDCRedirectURL": "Ohjaus URL oAuth-toimittajalle",
|
||||
"settings.security.OIDCRedirectWarning": "Tämä ei vaikuta olevan tuotantoympäristön URL-osoite. Vaihda URL-osoite 'Yleiset' asetuksissa.",
|
||||
"settings.security.OIDCURL": "Toimittajan URL",
|
||||
"settings.security.OIDCWarning": "Kun OIDC on käytössä, oletussalasanasisäänkirjautuminen on poistettu käytöstä. Virheelliset asetukset voivat estää sisäänkirjautumisen.",
|
||||
"settings.security.altchaComplexity": "Altcha-monimutkaisuus",
|
||||
"settings.security.altchaComplexityHelp": "Korkeammat arvot tarjoavat paremman suojan mutta hidastavat ratkaisua (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com-sivutunnus",
|
||||
"settings.security.captchaKeyHelp": "Hanki avain ja salaisuus osoitteesta www.hcaptcha.com.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com-salaisuus",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Vastaanottajan e-mail",
|
||||
"settings.title": "Asetukset",
|
||||
"settings.updateAvailable": "Uusi päivitys {version} on saatavilla.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Edistynyt",
|
||||
"subscribers.advancedQueryHelp": "Osa SQL-lauseketta tilaajien ominaisuuksien kyselyä varten",
|
||||
"subscribers.attribs": "Ominaisuudet",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Kopioi API-avain nyt. Sitä ei näytetä uudelleen.",
|
||||
"users.cantDeleteRole": "Ei voida poistaa roolia, jota käytetään.",
|
||||
"users.firstTime": "Tämä on tuore asennus. Valitse käyttäjänimi ja salasana Super Admin-tilille.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Virheellinen käyttäjänimi tai salasana",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Virheellinen todennuspyyntö",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Viimeisin kirjautuminen",
|
||||
"users.listPerms": "Listojen käyttöoikeudet",
|
||||
"users.listPermsWarning": "lists:get_all tai lists:manage_all on käytössä, mikä korvaa listakohtaiset käyttöoikeudet",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Kirjaudu ulos",
|
||||
"users.needSuper": "Käyttäjiä ei päivitetty. Vähintään yksi aktiivinen Super Admin-käyttäjä on oltava.",
|
||||
"users.newListRole": "Uusi listarooli",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Uusi käyttäjä",
|
||||
"users.newUserRole": "Uusi käyttäjärooli",
|
||||
"users.password": "Salasana",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Toista salasana",
|
||||
"users.perms": "Käyttöoikeudet",
|
||||
"users.profile": "Profiili",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rooli | Roolit",
|
||||
"users.roleGroup": "Ryhmä",
|
||||
"users.roles": "Roolit",
|
||||
"users.status.disabled": "Poistettu käytöstä",
|
||||
"users.status.enabled": "Käytössä",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tyyppi",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Ylin ylläpitäjä",
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "abonnements sans retour",
|
||||
"email.data.info": "Vous trouverez un fichier au format JSON contenant l'ensemble des données enregistrées à votre sujet en pièce jointe. Il peut être visualisé dans un éditeur de texte.",
|
||||
"email.data.title": "Vos données personnelles",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirmer votre abonnement",
|
||||
"email.optin.confirmSubHelp": "Confirmez votre abonnement en cliquant sur le bouton ci-dessous :",
|
||||
"email.optin.confirmSubInfo": "Vous avez été ajouté·e aux listes suivantes :",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copier",
|
||||
"globals.buttons.delete": "Supprimer",
|
||||
"globals.buttons.deleteAll": "Tout effacer",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Éditer",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Activé",
|
||||
"globals.buttons.insert": "Insérer",
|
||||
"globals.buttons.learnMore": "En savoir plus",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Champs non valides : {name}",
|
||||
"globals.messages.invalidID": "ID invalide",
|
||||
"globals.messages.invalidUUID": "UUID invalide",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Champ(s) manquant(s) : {name}",
|
||||
"globals.messages.notFound": "{name} introuvable",
|
||||
"globals.messages.numSelected": "{num} sélectionné(s)",
|
||||
"globals.messages.passwordChange": "Entrez un nouveau mot de passe pour en changer",
|
||||
"globals.messages.passwordChangeFull": "Effacer et saisir à nouveau le mot de passe complet dans '{name}'.",
|
||||
"globals.messages.permissionDenied": "Accès refusé : {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Modèle | Modèles",
|
||||
"globals.terms.templates": "Modèles",
|
||||
"globals.terms.tx": "Transactionnel | Transactionnels",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Utilisateur | Utilisateurs",
|
||||
"globals.terms.users": "Utilisateurs",
|
||||
"globals.terms.year": "Année | Années",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Type de fichier non pris en charge ({type})",
|
||||
"media.upload": "Importer",
|
||||
"media.uploadHelp": "Cliquez ou glissez-déposez ici une ou plusieurs image(s)",
|
||||
"media.uploadImage": "Importer une image",
|
||||
"menu.allCampaigns": "Toutes les campagnes",
|
||||
"menu.allLists": "Toutes les listes",
|
||||
"menu.allSubscribers": "Tou·tes les abonné·es",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Enregistrer l'adresse IP d'inscription",
|
||||
"settings.privacy.recordOptinIPHelp": "Enregistre l'adresse IP des double opt-ins dans les attributs des abonnés.",
|
||||
"settings.restart": "Redémarrer",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Créer les utilisateurs automatiquement",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Créer automatiquement un utilisateur lors de la première connexion si le compte n'existe pas.",
|
||||
"settings.security.OIDCClientID": "ID client",
|
||||
"settings.security.OIDCClientSecret": "Secret client",
|
||||
"settings.security.OIDCDefaultListRole": "Rôle de liste par défaut",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Rôle par défaut attribué aux utilisateurs créés automatiquement via OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Rôle utilisateur par défaut",
|
||||
"settings.security.OIDCHelp": "Activer l'authentification OpenID Connect OAuth2 via un fournisseur OAuth.",
|
||||
"settings.security.OIDCName": "Provider name",
|
||||
"settings.security.OIDCRedirectURL": "URL de redirection pour le fournisseur oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Cela ne semble pas être une URL de production. Modifiez l'URL racine dans les paramètres 'Général'.",
|
||||
"settings.security.OIDCURL": "URL du fournisseur",
|
||||
"settings.security.OIDCWarning": "Lorsque OIDC est activé, la connexion par mot de passe par défaut est désactivée. Une configuration invalide peut vous bloquer.",
|
||||
"settings.security.altchaComplexity": "Complexité Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Des valeurs plus élevées offrent une meilleure sécurité mais une résolution plus lente (1000-1000000).",
|
||||
"settings.security.captchaKey": "Clef de site hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Allez sur www.hcaptcha.com pour obtenir une clef et son secret.",
|
||||
"settings.security.captchaSecret": "Secret hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Courriel du destinataire",
|
||||
"settings.title": "Paramètres",
|
||||
"settings.updateAvailable": "Une nouvelle version ({version}) est disponible.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Requête avancée",
|
||||
"subscribers.advancedQueryHelp": "Expression SQL partielle pour interroger les attributs de l'abonné·e",
|
||||
"subscribers.attribs": "Attributs",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copiez le jeton d'accès API maintenant. Il ne sera plus affiché.",
|
||||
"users.cantDeleteRole": "Impossible de supprimer un rôle utilisé.",
|
||||
"users.firstTime": "Ceci est une nouvelle installation. Choisissez un nom d'utilisateur et un mot de passe pour le compte Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Identifiant ou mot de passe invalide",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Requête d'authentification invalide",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Dernière connexion",
|
||||
"users.listPerms": "Permissions des listes",
|
||||
"users.listPermsWarning": "les autorisations lists:get_all ou lists:manage_all sont activées et remplacent les autorisations par liste",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Déconnecter",
|
||||
"users.needSuper": "Impossible de mettre à jour l'utilisateur (les). Il doit y avoir au moins un utilisateur Super Admin actif.",
|
||||
"users.newListRole": "Nouveau rôle de liste",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nouvel utilisateur",
|
||||
"users.newUserRole": "Nouveau rôle utilisateur",
|
||||
"users.password": "Mot de passe",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Répéter le mot de passe",
|
||||
"users.perms": "Permissions",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rôle | Rôles",
|
||||
"users.roleGroup": "Groupe",
|
||||
"users.roles": "Rôles",
|
||||
"users.status.disabled": "Désactivé",
|
||||
"users.status.enabled": "Activé",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Type",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/fr.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "abonnements sans retour",
|
||||
"email.data.info": "Vous trouverez un fichier au format JSON contenant l'ensemble des données enregistrées à votre sujet en pièce jointe. Il peut être visualisé dans un éditeur de texte.",
|
||||
"email.data.title": "Vos données personnelles",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirmer votre abonnement",
|
||||
"email.optin.confirmSubHelp": "Confirmez votre abonnement en cliquant sur le bouton ci-dessous :",
|
||||
"email.optin.confirmSubInfo": "Vous avez été ajouté·e aux listes suivantes :",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copier",
|
||||
"globals.buttons.delete": "Supprimer",
|
||||
"globals.buttons.deleteAll": "Tout effacer",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Éditer",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Activé",
|
||||
"globals.buttons.insert": "Insérer",
|
||||
"globals.buttons.learnMore": "En savoir plus",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Champs non valides : {name}",
|
||||
"globals.messages.invalidID": "ID invalide",
|
||||
"globals.messages.invalidUUID": "UUID invalide",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Champ(s) manquant(s) : {name}",
|
||||
"globals.messages.notFound": "{name} introuvable",
|
||||
"globals.messages.numSelected": "{num} sélectionné(s)",
|
||||
"globals.messages.passwordChange": "Entrez un nouveau mot de passe pour en changer",
|
||||
"globals.messages.passwordChangeFull": "Effacer et saisir à nouveau le mot de passe complet dans '{name}'.",
|
||||
"globals.messages.permissionDenied": "Autorisation refusée : {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Modèle | Modèles",
|
||||
"globals.terms.templates": "Modèles",
|
||||
"globals.terms.tx": "Transactionnel | Transactionnels",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Utilisateur | Utilisateurs",
|
||||
"globals.terms.users": "Utilisateurs",
|
||||
"globals.terms.year": "Année | Années",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Type de fichier non pris en charge ({type})",
|
||||
"media.upload": "Importer",
|
||||
"media.uploadHelp": "Cliquez ou glissez-déposez ici une ou plusieurs image(s)",
|
||||
"media.uploadImage": "Importer une image",
|
||||
"menu.allCampaigns": "Toutes les campagnes",
|
||||
"menu.allLists": "Toutes les listes",
|
||||
"menu.allSubscribers": "Tou·tes les abonné·es",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Enregistrer l'adresse IP d'inscription",
|
||||
"settings.privacy.recordOptinIPHelp": "Enregistre l'adresse IP des double opt-ins dans les attributs des abonnés.",
|
||||
"settings.restart": "Redémarrer",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Création automatique des utilisateurs",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Créer automatiquement un utilisateur lors de la première connexion si le compte n'existe pas.",
|
||||
"settings.security.OIDCClientID": "ID client",
|
||||
"settings.security.OIDCClientSecret": "Secret client",
|
||||
"settings.security.OIDCDefaultListRole": "Rôle de liste par défaut",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Rôle par défaut attribué aux utilisateurs créés automatiquement via OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Rôle utilisateur par défaut",
|
||||
"settings.security.OIDCHelp": "Activer la connexion OIDC via un fournisseur OAuth2.",
|
||||
"settings.security.OIDCName": "Provider name",
|
||||
"settings.security.OIDCRedirectURL": "URL de redirection pour le fournisseur oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Ceci ne semble pas être une URL de production. Modifiez l'URL Racine dans les paramètres 'Généraux'.",
|
||||
"settings.security.OIDCURL": "URL du fournisseur",
|
||||
"settings.security.OIDCWarning": "Lorsque OIDC est activé, la connexion par mot de passe par défaut est désactivée. Une configuration incorrecte peut vous empêcher d'accéder.",
|
||||
"settings.security.altchaComplexity": "Complexité Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Des valeurs plus élevées offrent une meilleure sécurité mais un temps de résolution plus long (1000-1000000).",
|
||||
"settings.security.captchaKey": "Clef de site hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Allez sur www.hcaptcha.com pour obtenir une clef et son secret.",
|
||||
"settings.security.captchaSecret": "Secret hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "E-mail du destinataire",
|
||||
"settings.title": "Paramètres",
|
||||
"settings.updateAvailable": "Une nouvelle version ({version}) est disponible.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Requête avancée",
|
||||
"subscribers.advancedQueryHelp": "Expression SQL partielle pour interroger les attributs de l'abonné·e",
|
||||
"subscribers.attribs": "Attributs",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copiez dès maintenant le jeton d'accès API. Il ne sera plus affiché.",
|
||||
"users.cantDeleteRole": "Impossible de supprimer un rôle en cours d'utilisation.",
|
||||
"users.firstTime": "Il s'agit d'une nouvelle installation. Choisissez un nom d'utilisateur et un mot de passe pour le compte Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Identifiant ou mot de passe incorrect",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Demande d'authentification invalide",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Dernière connexion",
|
||||
"users.listPerms": "Permissions de liste",
|
||||
"users.listPermsWarning": "les autorisations lists:get_all ou lists:manage_all sont activées, ce qui remplace les permissions par liste",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Déconnecter",
|
||||
"users.needSuper": "Utilisateur(s) ne peut être mis à jour. Il doit y avoir au moins un utilisateur Super Admin actif.",
|
||||
"users.newListRole": "Nouveau rôle de liste",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nouvel utilisateur",
|
||||
"users.newUserRole": "Nouveau rôle utilisateur",
|
||||
"users.password": "Mot de passe",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Répétez le mot de passe",
|
||||
"users.perms": "Autorisations",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rôle | Rôles",
|
||||
"users.roleGroup": "Groupe",
|
||||
"users.roles": "Rôles",
|
||||
"users.status.disabled": "Désactivé",
|
||||
"users.status.enabled": "Activé",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Type",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/he.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "יתומים",
|
||||
"email.data.info": "עותק של כל הנתונים הרשומים עליך מוצורף כקובץ בפורמט JSON. ניתן להציגו בעורך טקסט.",
|
||||
"email.data.title": "הנתונים שלך",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "אשר רישום",
|
||||
"email.optin.confirmSubHelp": "אשר את המינוי שלך על ידי לחיצה על הכפתור למטה.",
|
||||
"email.optin.confirmSubInfo": "נוספת בהצלחה לרשימת הבאות:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "העתק",
|
||||
"globals.buttons.delete": "מחיקה",
|
||||
"globals.buttons.deleteAll": "מחק הכל",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "עריכה",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "מופעל",
|
||||
"globals.buttons.insert": "להכניס",
|
||||
"globals.buttons.learnMore": "למד עוד",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "שדות לא חוקיים: {name}",
|
||||
"globals.messages.invalidID": "מזהים לא חוקיים",
|
||||
"globals.messages.invalidUUID": "UUID לא תקין (ים)",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "חסרים שדות: {name}",
|
||||
"globals.messages.notFound": "{name} לא נמצא",
|
||||
"globals.messages.numSelected": "{num} נבחרו",
|
||||
"globals.messages.passwordChange": "הזן ערך לשינוי",
|
||||
"globals.messages.passwordChangeFull": "נא לנקות ולהזין שוב את הסיסמה המלאה ב־'{name}'.",
|
||||
"globals.messages.permissionDenied": "הרשאה נדחתה: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "תבנית | תבניות",
|
||||
"globals.terms.templates": "תבניות",
|
||||
"globals.terms.tx": "עסקה | עסקה",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "משתמש | משתמשים",
|
||||
"globals.terms.users": "משתמשים",
|
||||
"globals.terms.year": "שנה | שנים",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "סוג קובץ לא נתמך ({type})",
|
||||
"media.upload": "העלאה",
|
||||
"media.uploadHelp": "לחץ או גרור לכאן תמונות",
|
||||
"media.uploadImage": "העלה תמונה",
|
||||
"menu.allCampaigns": "כל הקמפיינים",
|
||||
"menu.allLists": "כל הרשימות",
|
||||
"menu.allSubscribers": "כל הרשומים",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "תצורת דין רישום IP הפעילה",
|
||||
"settings.privacy.recordOptinIPHelp": "תיחום כתובת ה־IP של רישום הפעילה החזקה במאפייני המנוי.",
|
||||
"settings.restart": "הפעלה מחדש",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "יצירת משתמשים אוטומטית",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "יצירת משתמש אוטומטית בעת ההתחברות הראשונה אם החשבון לא קיים.",
|
||||
"settings.security.OIDCClientID": "זהות לקוח (Client ID)",
|
||||
"settings.security.OIDCClientSecret": "סוד לקוח (Client secret)",
|
||||
"settings.security.OIDCDefaultListRole": "תפקיד רשימה ברירת מחדל",
|
||||
"settings.security.OIDCDefaultRoleHelp": "תפקיד ברירת מחדל שיוקצה למשתמשים שנוצרים אוטומטית מ-OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "תפקיד משתמש ברירת מחדל",
|
||||
"settings.security.OIDCHelp": "הפעלת התחברות OpenID Connect OAuth2 דרך ספק OAuth.",
|
||||
"settings.security.OIDCName": "שם הספק",
|
||||
"settings.security.OIDCRedirectURL": "כתובת URL למפנה של ספק ה- oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "זה לא נראה ככתובת URL פעילה. שנה את כתובת השורש בהגדרות 'כלליות'.",
|
||||
"settings.security.OIDCURL": "כתובת URL של ספק",
|
||||
"settings.security.OIDCWarning": "כאשר OIDC מופעל, התחברות בברירת מחדל בעזרת סיסמה מבוטלת. הגדרות שגויות עלולות לנעול אותך בחוץ.",
|
||||
"settings.security.altchaComplexity": "מורכבות Altcha",
|
||||
"settings.security.altchaComplexityHelp": "ערכים גבוהים יותר מספקים אבטחה טובה יותר אך פתרון איטי יותר (1000-1000000).",
|
||||
"settings.security.captchaKey": "מפתח אתר של hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "אין להתרשם הפעלה על מנת לקבל את מפתח המקוד והסוד שלך.",
|
||||
"settings.security.captchaSecret": "סוד מאיש הגזיון",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "לכתובת",
|
||||
"settings.title": "הגדרות",
|
||||
"settings.updateAvailable": "עדכון חדש {version} זמין.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "מתקדם",
|
||||
"subscribers.advancedQueryHelp": "הביטוי הדו־לשוני הוא להשתמש בביטוי SQL חלקיאָני לחיפוש אחריות במאפיינים בעלי חיפוש מתקדם.",
|
||||
"subscribers.attribs": "מאפיינים",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "העתק עכשיו את אסימון גישה ל-API. לא יוצג שוב.",
|
||||
"users.cantDeleteRole": "אין אפשרות למחוק תפקיד הנמצא בשימוש.",
|
||||
"users.firstTime": "זוהי התקנה חדשה. בחר שם משתמש וסיסמה לחשבון הניהול העליון.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "התחברות או סיסמה לא תקינים",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "בקשת אימות לא חוקית",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "התחברות אחרונה",
|
||||
"users.listPerms": "הרשאות רשימות",
|
||||
"users.listPermsWarning": "הרשאות ל-get_all או ל-manage_all מופעלות ומעקב אחרי הרשאות של הרשימות נמחק.",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "התנתקות",
|
||||
"users.needSuper": "המשתמש(ים) לא יכולו להיות מעודכנים. חייב להיות לפחות משתמש אחד עם הרשאת מנהל עליון בתוקנה.",
|
||||
"users.newListRole": "תפקיד חדש לרשימה",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "משתמש חדש",
|
||||
"users.newUserRole": "תפקיד משתמש חדש",
|
||||
"users.password": "סיסמה",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "חזור על הסיסמה",
|
||||
"users.perms": "הרשאות",
|
||||
"users.profile": "פרופיל",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "תפקיד | תפקידים",
|
||||
"users.roleGroup": "קבוצה",
|
||||
"users.roles": "תפקידים",
|
||||
"users.status.disabled": "מנוטרל",
|
||||
"users.status.enabled": "מאופשר",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "סוג",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "מנהל עליון",
|
||||
|
||||
34
i18n/hu.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Árvák",
|
||||
"email.data.info": "A tagsággal nyilvántartott adatokat a JSON formátumú szövegfájlban küldött csatolmány tartalmazza.",
|
||||
"email.data.title": "A tagságra vonatkozó adatok",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Feliratkozás megerősítése",
|
||||
"email.optin.confirmSubHelp": "Erősítse meg tagságát a gombra kattintva.",
|
||||
"email.optin.confirmSubInfo": "Ön felkerült az alábbi listákra:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Másolás",
|
||||
"globals.buttons.delete": "Törlés",
|
||||
"globals.buttons.deleteAll": "Összes törlése",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Szerkesztés",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Engedélyezve",
|
||||
"globals.buttons.insert": "Beszúrás",
|
||||
"globals.buttons.learnMore": "Tudj meg többet",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Érvénytelen mező(k): {name}",
|
||||
"globals.messages.invalidID": "Érvénytelen azonosító(k)",
|
||||
"globals.messages.invalidUUID": "Érvénytelen UUID(-k)",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Hiányzó mező(k): {name}",
|
||||
"globals.messages.notFound": "{name} nem található",
|
||||
"globals.messages.numSelected": "{num} kiválasztva",
|
||||
"globals.messages.passwordChange": "Adja meg az új jelszót",
|
||||
"globals.messages.passwordChangeFull": "Törölje ki, és írja be újra a teljes jelszót a(z) '{name}'-ben.",
|
||||
"globals.messages.permissionDenied": "Engedély megtagadva: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Sablon",
|
||||
"globals.terms.templates": "Sablonok",
|
||||
"globals.terms.tx": "Ügymenet",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Felhasználó | Felhasználók",
|
||||
"globals.terms.users": "Felhasználók",
|
||||
"globals.terms.year": "Év",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Nem támogatott típus ({type})",
|
||||
"media.upload": "Feltöltés",
|
||||
"media.uploadHelp": "Kattintson vagy húzzon ide egy vagy több képet",
|
||||
"media.uploadImage": "Kép feltöltése",
|
||||
"menu.allCampaigns": "Minden kampány",
|
||||
"menu.allLists": "Minden lista",
|
||||
"menu.allSubscribers": "Minden tag",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "IP-cím rögzítésére feliratkozás",
|
||||
"settings.privacy.recordOptinIPHelp": "Az előfizető attribútumainak feljegyzésekor rögzítse a dupla opt-in IP címét.",
|
||||
"settings.restart": "Újraindítás",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Felhasználók automatikus létrehozása",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Felhasználó automatikus létrehozása az első bejelentkezéskor, ha a fiók nem létezik.",
|
||||
"settings.security.OIDCClientID": "Ügyfél-azonosító",
|
||||
"settings.security.OIDCClientSecret": "Ügyfél titka",
|
||||
"settings.security.OIDCDefaultListRole": "Alapértelmezett lista szerep",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Alapértelmezett szerep, amely OIDC-ből automatikusan létrehozott felhasználóknak van kiosztva.",
|
||||
"settings.security.OIDCDefaultUserRole": "Alapértelmezett felhasználói szerep",
|
||||
"settings.security.OIDCHelp": "Engedélyezze az OpenID Connect OAuth2 bejelentkezést egy OAuth-szolgáltatón keresztül.",
|
||||
"settings.security.OIDCName": "Szolgáltató neve",
|
||||
"settings.security.OIDCRedirectURL": "URL-cím átirányítása az oAuth-szolgáltatóhoz",
|
||||
"settings.security.OIDCRedirectWarning": "Úgy tűnik, ez nem egy éles URL-cím. Módosítsa a gyökér URL-címet az „Általános” beállításokban.",
|
||||
"settings.security.OIDCURL": "Szolgáltató URL-címe",
|
||||
"settings.security.OIDCWarning": "Ha az OIDC engedélyezve van, az alapértelmezett jelszó bejelentkezés le van tiltva. Az érvénytelen beállítás kizárhatja Önt.",
|
||||
"settings.security.altchaComplexity": "Altcha komplexitás",
|
||||
"settings.security.altchaComplexityHelp": "A magasabb érték jobb biztonságot nyújt, de lassabb megoldást eredményez (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com kulcs",
|
||||
"settings.security.captchaKeyHelp": "Kulcs és jelszó igénylése a hcaptcha.com oldalon.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com jelszó",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Címzett",
|
||||
"settings.title": "Beállítások",
|
||||
"settings.updateAvailable": "Új verzió érhető el! ({version})",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Adatbázis lekérdezés",
|
||||
"subscribers.advancedQueryHelp": "Részleges SQL kifejezés a tagok lekérdezéséhez",
|
||||
"subscribers.attribs": "Adatok",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Másolja ki most az API hozzáférési tokent. Nem jelenik meg újra.",
|
||||
"users.cantDeleteRole": "A használatban lévő szerepkör nem törölhető.",
|
||||
"users.firstTime": "Ez egy friss telepítés. Válasszon felhasználónevet és jelszót a Super Admin fiókhoz.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Érvénytelen bejelentkezési név vagy jelszó",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Érvénytelen hitelesítési kérelem",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Utolsó bejelentkezés",
|
||||
"users.listPerms": "Lista engedélyek",
|
||||
"users.listPermsWarning": "A lists:get_all vagy a lists:manage_all engedélyezve van, amely felülbírálja a listánkénti engedélyeket",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Kijelentkezés",
|
||||
"users.needSuper": "A felhasználó(k) nem tudták frissíteni. Legalább egy aktív Super Admin felhasználónak kell lennie.",
|
||||
"users.newListRole": "Új lista szerepkör",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Új felhasználó",
|
||||
"users.newUserRole": "Új felhasználói szereplő",
|
||||
"users.password": "Jelszó",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Jelszó ismétlése",
|
||||
"users.perms": "Engedélyek",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Szerepkör| Szerepkörök",
|
||||
"users.roleGroup": "Csoport",
|
||||
"users.roles": "Szerepkörök",
|
||||
"users.status.disabled": "Letiltva",
|
||||
"users.status.enabled": "Engedélyezve",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Típus",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Szuper rendszergazda",
|
||||
|
||||
94
i18n/it.json
@@ -4,15 +4,15 @@
|
||||
"admin.errorMarshallingConfig": "Errore durante la lettura della configurazione: {error}",
|
||||
"analytics.count": "Conteggio",
|
||||
"analytics.fromDate": "Da",
|
||||
"analytics.invalidDates": "Date `da` o `fino` invalide.",
|
||||
"analytics.invalidDates": "Date `da` o `fino` non valide.",
|
||||
"analytics.isUnique": "I conteggi sono unici per iscritto.",
|
||||
"analytics.links": "Link",
|
||||
"analytics.nonUnique": "I conteggi non sono univoci poiché il monitoraggio dei singoli iscritti è disattivato.",
|
||||
"analytics.title": "Analitiche",
|
||||
"analytics.title": "Analytics",
|
||||
"analytics.toDate": "a",
|
||||
"bounces.complaint": "Reclamo",
|
||||
"bounces.hard": "Bloccante",
|
||||
"bounces.soft": "Temporaneo",
|
||||
"bounces.hard": "Difficile",
|
||||
"bounces.soft": "Facile",
|
||||
"bounces.source": "Sorgente",
|
||||
"bounces.unknownService": "Servizio sconosciuto.",
|
||||
"bounces.view": "Visualizza i rimbalzi",
|
||||
@@ -20,9 +20,9 @@
|
||||
"campaigns.addAttachments": "Aggiungi allegati",
|
||||
"campaigns.archive": "Archivio",
|
||||
"campaigns.archiveEnable": "Rendere pubblico l'archivio",
|
||||
"campaigns.archiveHelp": "Pubblicare i messaggi delle campagne (avviate, pausate, finite) nel archivio pubblico.",
|
||||
"campaigns.archiveHelp": "Pubblicare i messaggi delle campagne (avviate, messe in pausa, terminate) nell'archivio pubblico.",
|
||||
"campaigns.archiveMeta": "Metadati della campagna",
|
||||
"campaigns.archiveMetaHelp": "Dati fittizi dell'iscritto da utilizzare nel messaggio pubblico, inclusi nome, e-mail ed eventuali attributi facoltativi utilizzati nel messaggio o nel modello della campagna.",
|
||||
"campaigns.archiveMetaHelp": "Dati fittizi dell'iscritto da utilizzare nel messaggio pubblico, inclusi nome, email ed eventuali attributi facoltativi utilizzati nel messaggio o nel modello della campagna.",
|
||||
"campaigns.archiveSlug": "Slug URL",
|
||||
"campaigns.archiveSlugHelp": "Un nome breve per la pagina da utilizzare nell'URL pubblico. es: mia-newsletter-edizione-2",
|
||||
"campaigns.attachments": "Allegati",
|
||||
@@ -38,12 +38,12 @@
|
||||
"campaigns.copyOf": "Copie di {name}",
|
||||
"campaigns.customHeadersHelp": "Lista di header personalizzati da allegare ai messaggi in uscita. eg: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"campaigns.dateAndTime": "Data e ora",
|
||||
"campaigns.ended": "Finito",
|
||||
"campaigns.ended": "Terminata",
|
||||
"campaigns.errorSendTest": "Errore durante il test di invio: {error}",
|
||||
"campaigns.fieldInvalidBody": "Errore durante la compilazione del contenuto della campagna: {error}",
|
||||
"campaigns.fieldInvalidFromEmail": "`Mittente` non valido.",
|
||||
"campaigns.fieldInvalidListIDs": "ID della lista non valido.",
|
||||
"campaigns.fieldInvalidMessenger": "Strumento di messaggeria sconosciuto {name}.",
|
||||
"campaigns.fieldInvalidMessenger": "Strumento di messaggistica sconosciuto {name}.",
|
||||
"campaigns.fieldInvalidName": "Lunghezza del nome non valida.",
|
||||
"campaigns.fieldInvalidSendAt": "La data programmata deve essere futura.",
|
||||
"campaigns.fieldInvalidSubject": "Lunghezza dell'oggetto non valida.",
|
||||
@@ -81,16 +81,16 @@
|
||||
"campaigns.send": "Inviare",
|
||||
"campaigns.sendLater": "Inviare più tardi",
|
||||
"campaigns.sendTest": "Inviare un messaggio di testo",
|
||||
"campaigns.sendTestHelp": "Per aggiungere più destinatari, premi Enter dopo aver aggiunto un indirizzo. Gli indirizzi devono appartenere a iscritti esistenti.",
|
||||
"campaigns.sendTestHelp": "Per aggiungere più destinatari, premi Invio dopo aver aggiunto un indirizzo. Gli indirizzi devono appartenere a iscritti esistenti.",
|
||||
"campaigns.sendToLists": "Liste da inviare a",
|
||||
"campaigns.sent": "Inviato",
|
||||
"campaigns.start": "Lanciare la campagna",
|
||||
"campaigns.started": "\"{name}\" ha cominciato",
|
||||
"campaigns.startedAt": "Cominciato",
|
||||
"campaigns.started": "\"{name}\" è iniziata",
|
||||
"campaigns.startedAt": "Iniziata",
|
||||
"campaigns.stats": "Statistiche",
|
||||
"campaigns.status.cancelled": "Annullato",
|
||||
"campaigns.status.cancelled": "Annullata",
|
||||
"campaigns.status.draft": "Bozza",
|
||||
"campaigns.status.finished": "Finito",
|
||||
"campaigns.status.finished": "Terminata",
|
||||
"campaigns.status.paused": "In pausa",
|
||||
"campaigns.status.running": "In corso",
|
||||
"campaigns.status.scheduled": "Programmata",
|
||||
@@ -99,7 +99,7 @@
|
||||
"campaigns.templatingRef": "Riferimento di Templating",
|
||||
"campaigns.testEmails": "Emails di prova",
|
||||
"campaigns.testSent": "Messaggio di prova inviato",
|
||||
"campaigns.timestamps": "Marcatura temporale ",
|
||||
"campaigns.timestamps": "Marcatura temporale",
|
||||
"campaigns.trackLink": "Link di tracciamento",
|
||||
"campaigns.unSchedule": "Annulla pianificazione",
|
||||
"campaigns.views": "Visualizzazioni",
|
||||
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Orfani",
|
||||
"email.data.info": "È stato aggiunto un file JSON contenente l'insieme dei tuoi dati salvati. Può essere visualizzato in un editore di testo.",
|
||||
"email.data.title": "I tuoi dati",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confermare l'iscrizione",
|
||||
"email.optin.confirmSubHelp": "Conferma la tua iscrizione cliccando sul pulsante qui sotto.",
|
||||
"email.optin.confirmSubInfo": "Sei stato aggiunto alle liste seguenti:",
|
||||
@@ -117,7 +120,7 @@
|
||||
"email.optin.confirmSubWelcome": "Buongiorno",
|
||||
"email.optin.privateList": "Lista privata",
|
||||
"email.status.campaignReason": "Ragione",
|
||||
"email.status.campaignSent": "Inviato",
|
||||
"email.status.campaignSent": "Inviata",
|
||||
"email.status.campaignUpdateTitle": "Aggiornamento della campagna",
|
||||
"email.status.importFile": "Archivio",
|
||||
"email.status.importRecords": "Salvataggi",
|
||||
@@ -144,8 +147,10 @@
|
||||
"globals.buttons.continue": "Continuare",
|
||||
"globals.buttons.copy": "Copia",
|
||||
"globals.buttons.delete": "Cancellare",
|
||||
"globals.buttons.deleteAll": "Cancellare tutto/e",
|
||||
"globals.buttons.deleteAll": "Cancellare tutto",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Modifica",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Attivata",
|
||||
"globals.buttons.insert": "Inserire",
|
||||
"globals.buttons.learnMore": "Per saperne di più",
|
||||
@@ -166,7 +171,7 @@
|
||||
"globals.days.5": "Gio",
|
||||
"globals.days.6": "Ven",
|
||||
"globals.days.7": "Sab",
|
||||
"globals.fields.createdAt": "Creato il ",
|
||||
"globals.fields.createdAt": "Creato il",
|
||||
"globals.fields.description": "Descrizione",
|
||||
"globals.fields.id": "ID",
|
||||
"globals.fields.name": "Nome",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Campi non validi: {name}",
|
||||
"globals.messages.invalidID": "ID non valido",
|
||||
"globals.messages.invalidUUID": "UUID non valido",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Valore/i mancante/i: {name}",
|
||||
"globals.messages.notFound": "{name} introvabile",
|
||||
"globals.messages.numSelected": "{num} selezionati",
|
||||
"globals.messages.passwordChange": "Inserisci un valore da modificare",
|
||||
"globals.messages.passwordChangeFull": "Cancella e reinserisci la password completa in '{name}'.",
|
||||
"globals.messages.permissionDenied": "Permesso negato: {name}",
|
||||
@@ -213,8 +220,8 @@
|
||||
"globals.months.8": "Ago",
|
||||
"globals.months.9": "Set",
|
||||
"globals.states.off": "Spenti",
|
||||
"globals.terms.all": "Tutti/e",
|
||||
"globals.terms.analytics": "Analitiche",
|
||||
"globals.terms.all": "Tutti",
|
||||
"globals.terms.analytics": "Analytics",
|
||||
"globals.terms.bounce": "Rimbalzo | Rimbalzi",
|
||||
"globals.terms.bounces": "Rimbalzi",
|
||||
"globals.terms.campaign": "Campagna | Campagne",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Modello | Modelli",
|
||||
"globals.terms.templates": "Modelli",
|
||||
"globals.terms.tx": "Transazionale | Transazionali",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Utente | Utenti",
|
||||
"globals.terms.users": "Utenti",
|
||||
"globals.terms.year": "Anno | Anni",
|
||||
@@ -263,7 +271,7 @@
|
||||
"import.invalidFile": "Archivio non valido: {error}",
|
||||
"import.invalidMode": "Modalità non valida",
|
||||
"import.invalidParams": "Parametri non validi: {error}",
|
||||
"import.invalidSubStatus": "Status della/e iscrizione/i non valida/e",
|
||||
"import.invalidSubStatus": "Stato dell'iscrizione/i non valida/e",
|
||||
"import.listSubHelp": "Liste a cui iscriversi.",
|
||||
"import.mode": "Modalità",
|
||||
"import.overwrite": "Sovrascrivere?",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Tipo di file non supportato ({type})",
|
||||
"media.upload": "Caricare",
|
||||
"media.uploadHelp": "Seleziona o trascina qui una o più immagini",
|
||||
"media.uploadImage": "Caricare l'immagine",
|
||||
"menu.allCampaigns": "Tutte le campagne",
|
||||
"menu.allLists": "Tutte le liste",
|
||||
"menu.allSubscribers": "Tutti gli iscritti",
|
||||
@@ -400,8 +407,8 @@
|
||||
"settings.bounces.sendgridKey": "Chiave SendGrid",
|
||||
"settings.bounces.type": "Tipo",
|
||||
"settings.bounces.username": "Nome utente",
|
||||
"settings.confirmRestart": "Assicurati che le campagne sono in pausa. Riavviare?",
|
||||
"settings.duplicateMessengerName": "Nome in messaggeria doppio: {name}",
|
||||
"settings.confirmRestart": "Assicurati che le campagne siano in pausa. Riavviare?",
|
||||
"settings.duplicateMessengerName": "Nome nella messaggistica doppio: {name}",
|
||||
"settings.errorEncoding": "Errore durante la codifica dei parametri: {error}",
|
||||
"settings.errorNoSMTP": "Devi attivare almeno un blocco SMTP",
|
||||
"settings.general.adminNotifEmails": "Mail di notifica amministratore",
|
||||
@@ -521,16 +528,25 @@
|
||||
"settings.privacy.listUnsubHeaderHelp": "Includere intestazioni di annullamento dell'iscrizione che consentono agli utenti di annullare l'iscrizione con un clic dal proprio client di posta elettronica.",
|
||||
"settings.privacy.name": "Privacy",
|
||||
"settings.privacy.recordOptinIP": "Registra l'indirizzo IP di consenso",
|
||||
"settings.privacy.recordOptinIPHelp": "Registra l'indirizzo IP dei double opt-in negli attributi dell'iscritto.",
|
||||
"settings.privacy.recordOptinIPHelp": "Registra l'indirizzo IP dei doppi opt-in negli attributi dell'iscritto.",
|
||||
"settings.restart": "Riavviare",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Crea utenti automaticamente",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Crea automaticamente un utente al primo accesso se l’account non esiste.",
|
||||
"settings.security.OIDCClientID": "ID client",
|
||||
"settings.security.OIDCClientSecret": "Segreto client",
|
||||
"settings.security.OIDCClientSecret": "Client segreto",
|
||||
"settings.security.OIDCDefaultListRole": "Ruolo lista predefinito",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Ruolo predefinito assegnato agli utenti creati automaticamente da OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Ruolo utente predefinito",
|
||||
"settings.security.OIDCHelp": "Abilita l'accesso OAuth2 con OpenID Connect OAuth2 tramite un provider OAuth.",
|
||||
"settings.security.OIDCName": "Nome provider",
|
||||
"settings.security.OIDCRedirectURL": "URL di reindirizzamento per il provider oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Questo non sembra essere un URL di produzione. Cambiare l'URL principale nelle impostazioni 'Generali'.",
|
||||
"settings.security.OIDCURL": "URL provider",
|
||||
"settings.security.OIDCWarning": "Quando OIDC è abilitato, il login con password predefinita è disabilitato. Una configurazione non valida può escludervi.",
|
||||
"settings.security.altchaComplexity": "Complessità Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Valori più alti forniscono maggiore sicurezza ma risoluzione più lenta (1000-1000000).",
|
||||
"settings.security.captchaKey": "Chiave sito hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Visita www.hcaptcha.com per ottenere la SiteKey e il secret.",
|
||||
"settings.security.captchaSecret": "Segreto hCaptcha.com",
|
||||
@@ -546,18 +562,19 @@
|
||||
"settings.smtp.name": "SMTP",
|
||||
"settings.smtp.retries": "Tentativi",
|
||||
"settings.smtp.retriesHelp": "Numero di tentativi in caso di errore invio messaggio.",
|
||||
"settings.smtp.sendTest": "Invia e-mail",
|
||||
"settings.smtp.sendTest": "Invia email",
|
||||
"settings.smtp.setCustomHeaders": "Definisci intestazioni personalizzate",
|
||||
"settings.smtp.testConnection": "Prova la connessione",
|
||||
"settings.smtp.testEnterEmail": "Inserire di nuovo la password per fare il test",
|
||||
"settings.smtp.toEmail": "Casella di posta di ricezione",
|
||||
"settings.title": "Impostazioni",
|
||||
"settings.updateAvailable": "È disponibile una nuova versione {version}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avanzate",
|
||||
"subscribers.advancedQueryHelp": "Espressione SQL parziale per interrogare gli attributi del sottoscrittore",
|
||||
"subscribers.attribs": "Attributi",
|
||||
"subscribers.attribsHelp": "Gli attributi sono definiti come un JSON, ad esempio:",
|
||||
"subscribers.blocklistedHelp": "Gli abbonati bloccati non riceveranno mai e-mail.",
|
||||
"subscribers.blocklistedHelp": "Gli abbonati bloccati non riceveranno mai email.",
|
||||
"subscribers.confirmBlocklist": "Lista di blocco {num} iscritto(i)?",
|
||||
"subscribers.confirmDelete": "Elimina {num} iscritto(i)?",
|
||||
"subscribers.confirmExport": "Esporta {num} iscritto(i)?",
|
||||
@@ -572,7 +589,7 @@
|
||||
"subscribers.errorSendingOptin": "Errore durante l'invio dell'e-mail di attivazione.",
|
||||
"subscribers.export": "Esportazione",
|
||||
"subscribers.invalidAction": "Azione non valida.",
|
||||
"subscribers.invalidEmail": "E-mail non valida.",
|
||||
"subscribers.invalidEmail": "Email non valida.",
|
||||
"subscribers.invalidJSON": "JSON non valido negli attributi.",
|
||||
"subscribers.invalidName": "Nome errato.",
|
||||
"subscribers.listChangeApplied": "Modifica della lista eseguita.",
|
||||
@@ -584,7 +601,7 @@
|
||||
"subscribers.newSubscriber": "Nuovo iscritto",
|
||||
"subscribers.numSelected": "{num} iscritto(i) selezionato(i)",
|
||||
"subscribers.optinSubject": "Confermare l'iscrizione",
|
||||
"subscribers.preconfirm": "Pre conferma l'iscrizione",
|
||||
"subscribers.preconfirm": "Pre-conferma l'iscrizione",
|
||||
"subscribers.preconfirmHelp": "Non inviate e-mail di opt-in e classifica tutte le iscrizioni alle liste come iscritti.",
|
||||
"subscribers.query": "Richiesta",
|
||||
"subscribers.queryPlaceholder": "Email o nome",
|
||||
@@ -618,18 +635,22 @@
|
||||
"users.apiOneTimeToken": "Copia ora il token di accesso API. Non verrà più mostrato.",
|
||||
"users.cantDeleteRole": "Impossibile eliminare il ruolo in uso.",
|
||||
"users.firstTime": "Questa è una installazione nuova. Scegliere un nome utente e una password per l'account Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Login o password non validi",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Richiesta di autorizzazione non valida",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Ultimo login",
|
||||
"users.listPerms": "Elenco permessi",
|
||||
"users.listPermsWarning": "Elenchi:get_all o lists:manage_all sono abilitati e sovrascrivono le autorizzazioni per elenco",
|
||||
"users.listRole": "Elenco ruoli | Elenco ruolo",
|
||||
"users.listRoles": "Elenco ruoli",
|
||||
"users.login": "Accesso",
|
||||
"users.login": "Accedere",
|
||||
"users.loginOIDC": "Login con {name}",
|
||||
"users.logout": "Esci",
|
||||
"users.logout": "Disconessione",
|
||||
"users.needSuper": "Impossibile aggiornare l'utente(i). Deve esserci almeno un utente Super Admin attivo.",
|
||||
"users.newListRole": "Nuovo ruolo di elenco",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nuovo utente",
|
||||
"users.newUserRole": "Nuovo ruolo utente",
|
||||
"users.password": "Password",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Repeti password",
|
||||
"users.perms": "Permessi",
|
||||
"users.profile": "Profilo",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Ruolo | Ruoli",
|
||||
"users.roleGroup": "Gruppo",
|
||||
"users.roles": "Ruoli",
|
||||
"users.status.disabled": "Disabilitato",
|
||||
"users.status.enabled": "Abilitato",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tipo",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/jp.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "オーファン",
|
||||
"email.data.info": "あなたについて記録されたすべてのデータのコピーがJSON形式のファイルとして添付されています。テキストエディタで閲覧可能です。",
|
||||
"email.data.title": "あなたのデータ",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "サブスクリプションを確認",
|
||||
"email.optin.confirmSubHelp": "下のボタンを押してサブスクリプションを確認する。",
|
||||
"email.optin.confirmSubInfo": "あなたは以下のリストに追加されました:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "コピー",
|
||||
"globals.buttons.delete": "消去",
|
||||
"globals.buttons.deleteAll": "全て消去",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "編集",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "有効",
|
||||
"globals.buttons.insert": "入れる",
|
||||
"globals.buttons.learnMore": "さらに詳しく",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "無効なフィールド:{name}",
|
||||
"globals.messages.invalidID": "無効なID",
|
||||
"globals.messages.invalidUUID": "無効なUUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "フィールドがありません: {name}",
|
||||
"globals.messages.notFound": "{name} が見つかりません。",
|
||||
"globals.messages.numSelected": "{num} 選択済み",
|
||||
"globals.messages.passwordChange": "変更するには値を入力",
|
||||
"globals.messages.passwordChangeFull": "'{name}’でパスワードをクリアして再入力してください。",
|
||||
"globals.messages.permissionDenied": "権限が拒否されました:{name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "テンプレート | テンプレート",
|
||||
"globals.terms.templates": "テンプレート",
|
||||
"globals.terms.tx": "トランザクションメール | トランザクションメール",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "ユーザー | ユーザー",
|
||||
"globals.terms.users": "ユーザー",
|
||||
"globals.terms.year": "都市 | 都市",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "サポートされていないファイルタイプ ({type})",
|
||||
"media.upload": "アップロード",
|
||||
"media.uploadHelp": "ここに一枚か複数の画像をクリック、又はドラックしてください。",
|
||||
"media.uploadImage": "画像のアップロード",
|
||||
"menu.allCampaigns": "全てのキャンペーン",
|
||||
"menu.allLists": "全てのリスト",
|
||||
"menu.allSubscribers": "全ての加入者",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "オプトインIPアドレスを記録する",
|
||||
"settings.privacy.recordOptinIPHelp": "購読者属性にダブルオプトインのIPアドレスを記録します。",
|
||||
"settings.restart": "再起動",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "ユーザーの自動作成",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "アカウントが存在しない場合、初回ログイン時にユーザーを自動作成します。",
|
||||
"settings.security.OIDCClientID": "クライアントID",
|
||||
"settings.security.OIDCClientSecret": "クライアントシークレット",
|
||||
"settings.security.OIDCDefaultListRole": "デフォルトリストロール",
|
||||
"settings.security.OIDCDefaultRoleHelp": "OIDCから自動作成されたユーザーに割り当てるデフォルトロール。",
|
||||
"settings.security.OIDCDefaultUserRole": "デフォルトユーザーロール",
|
||||
"settings.security.OIDCHelp": "OAuthプロバイダを介したOpenID Connect OAuth2ログインを有効にします。",
|
||||
"settings.security.OIDCName": "プロバイダー名",
|
||||
"settings.security.OIDCRedirectURL": "oAuthプロバイダのリダイレクトURL",
|
||||
"settings.security.OIDCRedirectWarning": "これは本番URLではないようです。'一般'設定のルートURLを変更してください。",
|
||||
"settings.security.OIDCURL": "プロバイダURL",
|
||||
"settings.security.OIDCWarning": "OIDCが有効になっている場合、デフォルトのパスワードログインは無効になります。無効な設定はアカウントロックの原因になります。",
|
||||
"settings.security.altchaComplexity": "Altchaの複雑さ",
|
||||
"settings.security.altchaComplexityHelp": "値が大きいほどセキュリティは高くなりますが、解決に時間がかかります(1000〜1000000)。",
|
||||
"settings.security.captchaKey": "hCaptcha.comのサイトキー",
|
||||
"settings.security.captchaKeyHelp": "キーとシークレットを取得するには、www.hcaptcha.comを訪問してください。",
|
||||
"settings.security.captchaSecret": "hCaptcha.comシークレット",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "メール宛",
|
||||
"settings.title": "設定",
|
||||
"settings.updateAvailable": "新しい {version} の更新が可能です。",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "アドバンスド",
|
||||
"subscribers.advancedQueryHelp": "加入者属性を問い合わせる部分的なSQL式",
|
||||
"subscribers.attribs": "属性",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "APIアクセストークンを今すぐコピーしてください。もう表示されません。",
|
||||
"users.cantDeleteRole": "使用中のロールを削除することはできません。",
|
||||
"users.firstTime": "これは新しいインストールです。スーパーアドミンアカウントのユーザー名とパスワードを選択してください。",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "ログインまたはパスワードが無効です",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "無効な認証リクエストです",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "最終ログイン",
|
||||
"users.listPerms": "リストの権限",
|
||||
"users.listPermsWarning": "lists:get_allまたはlists:manage_allが有効になっており、これはリストごとの権限を上書きします",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "ログアウト",
|
||||
"users.needSuper": "ユーザー(複数可)の更新に失敗しました。少なくとも1人のアクティブなスーパーアドミンユーザーが必要です。",
|
||||
"users.newListRole": "新しいリストロール",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "新しいユーザー",
|
||||
"users.newUserRole": "新しいユーザーロール",
|
||||
"users.password": "パスワード",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "パスワードの繰り返し",
|
||||
"users.perms": "権限",
|
||||
"users.profile": "プロフィール",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "ロール | ロール",
|
||||
"users.roleGroup": "グループ",
|
||||
"users.roles": "ロール",
|
||||
"users.status.disabled": "無効",
|
||||
"users.status.enabled": "有効",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "タイプ",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "スーパーアドミン",
|
||||
|
||||
686
i18n/ko.json
Normal file
@@ -0,0 +1,686 @@
|
||||
{
|
||||
"_.code": "ko",
|
||||
"_.name": "한국어 (ko)",
|
||||
"admin.errorMarshallingConfig": "마샬링 설정 오류: {error}",
|
||||
"analytics.count": "카운트",
|
||||
"analytics.fromDate": "시작일",
|
||||
"analytics.invalidDates": "잘못된 `시작일` 또는 `종료일` 날짜입니다.",
|
||||
"analytics.isUnique": "카운트는 구독자별로 고유합니다.",
|
||||
"analytics.links": "링크",
|
||||
"analytics.nonUnique": "개별 구독자 추적이 꺼져 있어 카운트가 고유하지 않습니다.",
|
||||
"analytics.title": "분석",
|
||||
"analytics.toDate": "종료일",
|
||||
"bounces.complaint": "컴플레인",
|
||||
"bounces.hard": "하드 바운스",
|
||||
"bounces.soft": "소프트 바운스",
|
||||
"bounces.source": "소스",
|
||||
"bounces.unknownService": "알 수 없는 서비스.",
|
||||
"bounces.view": "바운스 보기",
|
||||
"campaigns.addAltText": "대체 일반 텍스트 메시지 추가",
|
||||
"campaigns.addAttachments": "첨부파일 추가",
|
||||
"campaigns.archive": "아카이브",
|
||||
"campaigns.archiveEnable": "공개 아카이브에 게시",
|
||||
"campaigns.archiveHelp": "공개 아카이브에 캠페인 메시지를 게시합니다 (진행 중, 일시정지, 완료 모두 포함).",
|
||||
"campaigns.archiveMeta": "캠페인 메타데이터",
|
||||
"campaigns.archiveMetaHelp": "캠페인 메시지나 템플릿에 사용할 더미 구독자 데이터(이름, 이메일, 속성 등).",
|
||||
"campaigns.archiveSlug": "URL 슬러그",
|
||||
"campaigns.archiveSlugHelp": "공개 URL에서 사용할 페이지의 짧은 이름. 예: my-newsletter-edition-2",
|
||||
"campaigns.attachments": "첨부파일",
|
||||
"campaigns.cantUpdate": "진행 중이거나 완료된 캠페인은 수정할 수 없습니다.",
|
||||
"campaigns.clicks": "클릭",
|
||||
"campaigns.confirmDelete": "{name} 삭제",
|
||||
"campaigns.confirmOverwriteContent": "이 작업은 모든 내용을 덮어씁니다. 계속하시겠습니까?",
|
||||
"campaigns.confirmSchedule": "이 캠페인은 예약된 날짜와 시간에 자동으로 시작됩니다. 지금 예약할까요?",
|
||||
"campaigns.confirmSwitchFormat": "내용의 서식이 깨질 수 있습니다. 계속하시겠습니까?",
|
||||
"campaigns.content": "콘텐츠",
|
||||
"campaigns.contentHelp": "여기에 콘텐츠 입력",
|
||||
"campaigns.continue": "계속",
|
||||
"campaigns.copyOf": "{name}의 복사본",
|
||||
"campaigns.customHeadersHelp": "발송 메시지에 첨부할 커스텀 헤더 배열. 예: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"campaigns.dateAndTime": "날짜 및 시간",
|
||||
"campaigns.ended": "종료됨",
|
||||
"campaigns.errorSendTest": "테스트 발송 오류: {error}",
|
||||
"campaigns.fieldInvalidBody": "캠페인 본문 컴파일 오류: {error}",
|
||||
"campaigns.fieldInvalidFromEmail": "잘못된 `from_email`.",
|
||||
"campaigns.fieldInvalidListIDs": "잘못된 리스트 ID.",
|
||||
"campaigns.fieldInvalidMessenger": "알 수 없는 메신저 {name}.",
|
||||
"campaigns.fieldInvalidName": "이름의 길이가 잘못되었습니다.",
|
||||
"campaigns.fieldInvalidSendAt": "예약 날짜는 미래여야 합니다.",
|
||||
"campaigns.fieldInvalidSubject": "제목의 길이가 잘못되었습니다.",
|
||||
"campaigns.format": "서식",
|
||||
"campaigns.formatHTML": "HTML 서식화",
|
||||
"campaigns.fromAddress": "발신자 주소",
|
||||
"campaigns.fromAddressPlaceholder": "이름 <noreply@yoursite.com>",
|
||||
"campaigns.importVisualTemplate": "비주얼 템플릿 가져오기",
|
||||
"campaigns.invalid": "잘못된 캠페인",
|
||||
"campaigns.invalidCustomHeaders": "잘못된 커스텀 헤더: {error}",
|
||||
"campaigns.markdown": "마크다운",
|
||||
"campaigns.needsSendAt": "캠페인 예약 날짜가 필요합니다.",
|
||||
"campaigns.newCampaign": "새 캠페인",
|
||||
"campaigns.noKnownSubsToTest": "테스트할 구독자가 없습니다.",
|
||||
"campaigns.noOptinLists": "캠페인 생성을 위한 옵트인 리스트가 없습니다.",
|
||||
"campaigns.noSubs": "선택한 리스트에 캠페인 생성 대상 구독자가 없습니다.",
|
||||
"campaigns.noSubsToTest": "대상 구독자가 없습니다.",
|
||||
"campaigns.notFound": "캠페인을 찾을 수 없습니다.",
|
||||
"campaigns.onlyActiveCancel": "진행 중인 캠페인만 취소할 수 있습니다.",
|
||||
"campaigns.onlyActivePause": "진행 중인 캠페인만 일시정지할 수 있습니다.",
|
||||
"campaigns.onlyDraftAsScheduled": "임시 저장 또는 일시정지된 캠페인만 예약할 수 있습니다.",
|
||||
"campaigns.onlyPausedDraft": "일시정지된 캠페인과 임시 저장만 시작할 수 있습니다.",
|
||||
"campaigns.onlyScheduledAsDraft": "예약된 캠페인만 임시 저장할 수 있습니다.",
|
||||
"campaigns.pause": "일시정지",
|
||||
"campaigns.plainText": "일반 텍스트",
|
||||
"campaigns.preview": "미리보기",
|
||||
"campaigns.progress": "진행률",
|
||||
"campaigns.queryPlaceholder": "이름 또는 제목",
|
||||
"campaigns.rateMinuteShort": "분",
|
||||
"campaigns.rawHTML": "원본 HTML",
|
||||
"campaigns.removeAltText": "대체 일반 텍스트 메시지 제거",
|
||||
"campaigns.richText": "리치 텍스트",
|
||||
"campaigns.schedule": "캠페인 예약",
|
||||
"campaigns.scheduled": "예약됨",
|
||||
"campaigns.send": "발송",
|
||||
"campaigns.sendLater": "나중에 발송",
|
||||
"campaigns.sendTest": "테스트 메시지 발송",
|
||||
"campaigns.sendTestHelp": "주소 입력 후 Enter를 눌러 여러 수신자를 추가하세요. 주소는 기존 구독자여야 합니다.",
|
||||
"campaigns.sendToLists": "발송 대상 리스트",
|
||||
"campaigns.sent": "발송됨",
|
||||
"campaigns.start": "캠페인 시작",
|
||||
"campaigns.started": "\"{name}\" 시작됨",
|
||||
"campaigns.startedAt": "시작됨",
|
||||
"campaigns.stats": "통계",
|
||||
"campaigns.status.cancelled": "취소됨",
|
||||
"campaigns.status.draft": "임시 저장",
|
||||
"campaigns.status.finished": "완료됨",
|
||||
"campaigns.status.paused": "일시정지됨",
|
||||
"campaigns.status.running": "진행 중",
|
||||
"campaigns.status.scheduled": "예약됨",
|
||||
"campaigns.statusChanged": "\"{name}\" {status}",
|
||||
"campaigns.subject": "제목",
|
||||
"campaigns.templatingRef": "템플릿 참조",
|
||||
"campaigns.testEmails": "이메일",
|
||||
"campaigns.testSent": "테스트 메시지 발송됨",
|
||||
"campaigns.timestamps": "타임스탬프",
|
||||
"campaigns.trackLink": "링크 추적",
|
||||
"campaigns.unSchedule": "예약 해제",
|
||||
"campaigns.views": "조회수",
|
||||
"campaigns.visual": "비주얼",
|
||||
"dashboard.campaignViews": "캠페인 조회수",
|
||||
"dashboard.linkClicks": "링크 클릭수",
|
||||
"dashboard.messagesSent": "발송된 메시지",
|
||||
"dashboard.orphanSubs": "누락된 구독자",
|
||||
"email.data.info": "귀하에 대해 기록된 모든 데이터의 복사본이 JSON 파일로 첨부되어 있습니다. 텍스트 에디터로 볼 수 있습니다.",
|
||||
"email.data.title": "내 데이터",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "구독 확인",
|
||||
"email.optin.confirmSubHelp": "아래 버튼을 클릭하여 구독을 확인하세요.",
|
||||
"email.optin.confirmSubInfo": "다음 리스트에 추가되었습니다:",
|
||||
"email.optin.confirmSubTitle": "구독 확인",
|
||||
"email.optin.confirmSubWelcome": "안녕하세요",
|
||||
"email.optin.privateList": "비공개 리스트",
|
||||
"email.status.campaignReason": "사유",
|
||||
"email.status.campaignSent": "발송됨",
|
||||
"email.status.campaignUpdateTitle": "캠페인 업데이트",
|
||||
"email.status.importFile": "파일",
|
||||
"email.status.importRecords": "기록",
|
||||
"email.status.importTitle": "가져오기 업데이트",
|
||||
"email.status.status": "상태",
|
||||
"email.unsub": "구독 해지",
|
||||
"email.unsubHelp": "이메일 수신을 원하지 않으신가요?",
|
||||
"email.viewInBrowser": "브라우저에서 보기",
|
||||
"forms.formHTML": "폼 HTML",
|
||||
"forms.formHTMLHelp": "외부 웹페이지에 구독 폼을 표시하려면 아래 HTML을 사용하세요. 폼에는 이메일 필드와 하나 이상의 `l`(리스트 UUID) 필드가 포함되어야 합니다. 이름 필드는 선택 사항입니다.",
|
||||
"forms.noPublicLists": "폼 생성을 위한 공개 리스트가 없습니다.",
|
||||
"forms.publicLists": "공개 리스트",
|
||||
"forms.publicSubPage": "공개 구독 페이지",
|
||||
"forms.selectHelp": "폼에 추가할 리스트를 선택하세요.",
|
||||
"forms.title": "폼",
|
||||
"globals.buttons.add": "추가",
|
||||
"globals.buttons.addNew": "새로 추가",
|
||||
"globals.buttons.back": "뒤로",
|
||||
"globals.buttons.cancel": "취소",
|
||||
"globals.buttons.clear": "지우기",
|
||||
"globals.buttons.clearAll": "전체 지우기",
|
||||
"globals.buttons.clone": "복제",
|
||||
"globals.buttons.close": "닫기",
|
||||
"globals.buttons.continue": "계속",
|
||||
"globals.buttons.copy": "복사",
|
||||
"globals.buttons.delete": "삭제",
|
||||
"globals.buttons.deleteAll": "전체 삭제",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "편집",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "활성화",
|
||||
"globals.buttons.insert": "삽입",
|
||||
"globals.buttons.learnMore": "자세히 보기",
|
||||
"globals.buttons.manage": "관리",
|
||||
"globals.buttons.more": "더보기",
|
||||
"globals.buttons.new": "새로 만들기",
|
||||
"globals.buttons.ok": "확인",
|
||||
"globals.buttons.remove": "제거",
|
||||
"globals.buttons.save": "저장",
|
||||
"globals.buttons.saveChanges": "변경사항 저장",
|
||||
"globals.buttons.toggleSelect": "선택 전환",
|
||||
"globals.buttons.view": "보기",
|
||||
"globals.days.0": "일",
|
||||
"globals.days.1": "일",
|
||||
"globals.days.2": "월",
|
||||
"globals.days.3": "화",
|
||||
"globals.days.4": "수",
|
||||
"globals.days.5": "목",
|
||||
"globals.days.6": "금",
|
||||
"globals.days.7": "토",
|
||||
"globals.fields.createdAt": "생성일",
|
||||
"globals.fields.description": "설명",
|
||||
"globals.fields.id": "ID",
|
||||
"globals.fields.name": "이름",
|
||||
"globals.fields.status": "상태",
|
||||
"globals.fields.type": "유형",
|
||||
"globals.fields.updatedAt": "수정일",
|
||||
"globals.fields.uuid": "UUID",
|
||||
"globals.messages.confirm": "정말로 진행하시겠습니까?",
|
||||
"globals.messages.confirmDiscard": "변경사항을 폐기하시겠습니까?",
|
||||
"globals.messages.copied": "복사됨",
|
||||
"globals.messages.created": "\"{name}\" 생성됨",
|
||||
"globals.messages.deleted": "\"{name}\" 삭제됨",
|
||||
"globals.messages.deletedCount": "{name} ({num}) 삭제됨",
|
||||
"globals.messages.done": "완료",
|
||||
"globals.messages.emptyState": "표시할 내용이 없습니다",
|
||||
"globals.messages.errorCreating": "{name} 생성 오류: {error}",
|
||||
"globals.messages.errorDeleting": "{name} 삭제 오류: {error}",
|
||||
"globals.messages.errorFetching": "{name} 불러오기 오류: {error}",
|
||||
"globals.messages.errorInvalidIDs": "하나 이상의 ID가 잘못되었습니다: {error}",
|
||||
"globals.messages.errorUUID": "UUID 생성 오류: {error}",
|
||||
"globals.messages.errorUpdating": "{name} 수정 오류: {error}",
|
||||
"globals.messages.internalError": "내부 서버 오류",
|
||||
"globals.messages.invalidData": "잘못된 데이터",
|
||||
"globals.messages.invalidFields": "잘못된 필드: {name}",
|
||||
"globals.messages.invalidID": "잘못된 ID",
|
||||
"globals.messages.invalidUUID": "잘못된 UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "누락된 필드: {name}",
|
||||
"globals.messages.notFound": "{name}을(를) 찾을 수 없습니다",
|
||||
"globals.messages.numSelected": "{num} 선택됨",
|
||||
"globals.messages.passwordChange": "변경할 값을 입력하세요",
|
||||
"globals.messages.passwordChangeFull": "'{name}'에서 비밀번호를 지우고 다시 입력하세요.",
|
||||
"globals.messages.permissionDenied": "권한 거부됨: {name}",
|
||||
"globals.messages.slowQueriesCached": "느린 쿼리가 캐시되고 있습니다. 이 페이지의 일부 수치는 최신이 아닐 수 있습니다.",
|
||||
"globals.messages.updated": "\"{name}\" 수정됨",
|
||||
"globals.months.1": "1월",
|
||||
"globals.months.10": "10월",
|
||||
"globals.months.11": "11월",
|
||||
"globals.months.12": "12월",
|
||||
"globals.months.2": "2월",
|
||||
"globals.months.3": "3월",
|
||||
"globals.months.4": "4월",
|
||||
"globals.months.5": "5월",
|
||||
"globals.months.6": "6월",
|
||||
"globals.months.7": "7월",
|
||||
"globals.months.8": "8월",
|
||||
"globals.months.9": "9월",
|
||||
"globals.states.off": "꺼짐",
|
||||
"globals.terms.all": "전체",
|
||||
"globals.terms.analytics": "분석",
|
||||
"globals.terms.bounce": "바운스",
|
||||
"globals.terms.bounces": "바운스",
|
||||
"globals.terms.campaign": "캠페인",
|
||||
"globals.terms.campaigns": "캠페인",
|
||||
"globals.terms.dashboard": "대시보드",
|
||||
"globals.terms.day": "일",
|
||||
"globals.terms.hour": "시간",
|
||||
"globals.terms.import": "가져오기",
|
||||
"globals.terms.list": "리스트",
|
||||
"globals.terms.lists": "리스트",
|
||||
"globals.terms.media": "미디어",
|
||||
"globals.terms.messenger": "메신저",
|
||||
"globals.terms.messengers": "메신저",
|
||||
"globals.terms.minute": "분",
|
||||
"globals.terms.month": "월",
|
||||
"globals.terms.new": "새로",
|
||||
"globals.terms.none": "없음",
|
||||
"globals.terms.second": "초",
|
||||
"globals.terms.settings": "설정",
|
||||
"globals.terms.subscriber": "구독자",
|
||||
"globals.terms.subscribers": "구독자",
|
||||
"globals.terms.subscriptions": "구독",
|
||||
"globals.terms.tag": "태그",
|
||||
"globals.terms.tags": "태그",
|
||||
"globals.terms.template": "템플릿",
|
||||
"globals.terms.templates": "템플릿",
|
||||
"globals.terms.tx": "트랜잭션",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "사용자",
|
||||
"globals.terms.users": "사용자",
|
||||
"globals.terms.year": "년",
|
||||
"import.alreadyRunning": "가져오기가 이미 실행 중입니다. 완료되거나 중지될 때까지 기다렸다가 다시 시도하세요.",
|
||||
"import.blocklist": "차단 목록",
|
||||
"import.csvDelim": "CSV 구분자",
|
||||
"import.csvDelimHelp": "기본 구분자는 쉼표입니다.",
|
||||
"import.csvExample": "CSV 예시",
|
||||
"import.csvFile": "CSV 또는 ZIP 파일",
|
||||
"import.csvFileHelp": "여기에 CSV 또는 ZIP 파일을 클릭하거나 드래그하세요.",
|
||||
"import.errorCopyingFile": "파일 복사 오류: {error}",
|
||||
"import.errorProcessingZIP": "ZIP 파일 처리 오류: {error}",
|
||||
"import.errorStarting": "가져오기 시작 오류: {error}",
|
||||
"import.importDone": "완료",
|
||||
"import.importStarted": "가져오기 시작됨",
|
||||
"import.instructions": "안내",
|
||||
"import.instructionsHelp": "구독자를 대량으로 가져오려면 CSV 파일 또는 하나의 CSV 파일이 포함된 ZIP 파일을 업로드하세요. CSV 파일에는 정확한 컬럼명이 포함된 아래 헤더가 필요합니다. attributes(선택 사항)는 이스케이프된 큰따옴표가 포함된 유효한 JSON 문자열이어야 합니다.",
|
||||
"import.invalidDelim": "구분자는 한 글자여야 합니다.",
|
||||
"import.invalidFile": "잘못된 파일: {error}",
|
||||
"import.invalidMode": "잘못된 모드",
|
||||
"import.invalidParams": "잘못된 파라미터: {error}",
|
||||
"import.invalidSubStatus": "잘못된 구독 상태",
|
||||
"import.listSubHelp": "구독할 리스트.",
|
||||
"import.mode": "모드",
|
||||
"import.overwrite": "덮어쓰기",
|
||||
"import.overwriteHelp": "기존 구독자의 이름, 속성, 구독 상태를 덮어쓸까요?",
|
||||
"import.recordsCount": "{num} / {total} 기록",
|
||||
"import.stopImport": "가져오기 중지",
|
||||
"import.subscribe": "구독",
|
||||
"import.subscribeWarning": "덮어쓰면 구독 해지된 이메일이 다시 구독됩니다. 계속하시겠습니까?",
|
||||
"import.title": "구독자 가져오기",
|
||||
"import.upload": "업로드",
|
||||
"lists.confirmDelete": "정말 삭제하시겠습니까? 구독자는 삭제되지 않습니다.",
|
||||
"lists.confirmSub": "{name} 구독 확인",
|
||||
"lists.invalidName": "잘못된 이름",
|
||||
"lists.newList": "새 리스트",
|
||||
"lists.optin": "옵트인",
|
||||
"lists.optinHelp": "더블 옵트인은 구독자에게 확인 이메일을 보냅니다. 더블 옵트인 리스트에서는 확인된 구독자에게만 캠페인이 발송됩니다.",
|
||||
"lists.optinTo": "{name} 옵트인",
|
||||
"lists.optins.double": "더블 옵트인",
|
||||
"lists.optins.single": "싱글 옵트인",
|
||||
"lists.sendCampaign": "캠페인 발송",
|
||||
"lists.sendOptinCampaign": "옵트인 캠페인 발송",
|
||||
"lists.type": "유형",
|
||||
"lists.typeHelp": "공개 리스트는 누구나 구독할 수 있으며, 이름이 구독 관리 페이지 등 공개 페이지에 표시될 수 있습니다.",
|
||||
"lists.types.private": "비공개",
|
||||
"lists.types.public": "공개",
|
||||
"logs.title": "로그",
|
||||
"maintenance.help": "데이터 양에 따라 작업 완료까지 시간이 걸릴 수 있습니다.",
|
||||
"maintenance.maintenance.unconfirmedOptins": "미확인 옵트인 구독",
|
||||
"maintenance.olderThan": "이전",
|
||||
"maintenance.orphanHelp": "누락된 구독자 = 어떤 리스트에도 포함되지 않은 구독자",
|
||||
"maintenance.title": "유지보수",
|
||||
"maintenance.unconfirmedSubs": "{name}일 이상 미확인 구독",
|
||||
"media.errorReadingFile": "파일 읽기 오류: {error}",
|
||||
"media.errorResizing": "이미지 크기 조정 오류: {error}",
|
||||
"media.errorSavingThumbnail": "썸네일 저장 오류: {error}",
|
||||
"media.errorUploading": "파일 업로드 오류: {error}",
|
||||
"media.invalidFile": "잘못된 파일: {error}",
|
||||
"media.title": "미디어",
|
||||
"media.unsupportedFileType": "지원하지 않는 파일 형식({type})",
|
||||
"media.upload": "업로드",
|
||||
"media.uploadHelp": "여기에 하나 이상의 이미지를 클릭하거나 드래그하세요.",
|
||||
"menu.allCampaigns": "전체 캠페인",
|
||||
"menu.allLists": "전체 리스트",
|
||||
"menu.allSubscribers": "전체 구독자",
|
||||
"menu.dashboard": "대시보드",
|
||||
"menu.forms": "폼",
|
||||
"menu.import": "가져오기",
|
||||
"menu.logs": "로그",
|
||||
"menu.maintenance": "유지보수",
|
||||
"menu.media": "미디어",
|
||||
"menu.newCampaign": "새로 만들기",
|
||||
"menu.settings": "설정",
|
||||
"public.archiveEmpty": "아카이브된 메시지가 없습니다.",
|
||||
"public.archiveTitle": "메일링 리스트 아카이브",
|
||||
"public.blocklisted": "영구 구독 해지됨.",
|
||||
"public.campaignNotFound": "이메일 메시지를 찾을 수 없습니다.",
|
||||
"public.confirmOptinSubTitle": "구독 확인",
|
||||
"public.confirmSub": "구독 확인",
|
||||
"public.confirmSubInfo": "다음 리스트에 추가되었습니다:",
|
||||
"public.confirmSubTitle": "확인",
|
||||
"public.dataRemoved": "구독 및 관련 데이터가 모두 삭제되었습니다.",
|
||||
"public.dataRemovedTitle": "데이터 삭제됨",
|
||||
"public.dataSent": "데이터가 첨부파일로 이메일로 전송되었습니다.",
|
||||
"public.dataSentTitle": "데이터 이메일 발송됨",
|
||||
"public.errorFetchingCampaign": "이메일 메시지 불러오기 오류.",
|
||||
"public.errorFetchingEmail": "이메일 메시지를 찾을 수 없음",
|
||||
"public.errorFetchingLists": "리스트 불러오기 오류. 다시 시도하세요.",
|
||||
"public.errorProcessingRequest": "요청 처리 오류. 다시 시도하세요.",
|
||||
"public.errorTitle": "오류",
|
||||
"public.invalidCaptcha": "잘못된 CAPTCHA.",
|
||||
"public.invalidFeature": "해당 기능을 사용할 수 없습니다.",
|
||||
"public.invalidLink": "잘못된 링크",
|
||||
"public.managePrefs": "환경설정 관리",
|
||||
"public.managePrefsUnsub": "체크 해제 시 해당 리스트 구독이 해지됩니다.",
|
||||
"public.noListsAvailable": "구독 가능한 리스트가 없습니다.",
|
||||
"public.noListsSelected": "구독할 유효한 리스트가 선택되지 않았습니다.",
|
||||
"public.noSubInfo": "확인할 구독이 없습니다.",
|
||||
"public.noSubTitle": "구독 없음",
|
||||
"public.notFoundTitle": "찾을 수 없음",
|
||||
"public.poweredBy": "Powered by",
|
||||
"public.prefsSaved": "환경설정이 저장되었습니다.",
|
||||
"public.privacyConfirmWipe": "모든 구독 데이터를 영구적으로 삭제하시겠습니까?",
|
||||
"public.privacyExport": "내 데이터 내보내기",
|
||||
"public.privacyExportHelp": "데이터 복사본이 이메일로 전송됩니다.",
|
||||
"public.privacyTitle": "개인정보 및 데이터",
|
||||
"public.privacyWipe": "내 데이터 삭제",
|
||||
"public.privacyWipeHelp": "모든 구독 및 관련 데이터를 영구적으로 삭제합니다.",
|
||||
"public.sub": "구독",
|
||||
"public.subConfirmed": "구독이 완료되었습니다.",
|
||||
"public.subConfirmedTitle": "확인됨",
|
||||
"public.subName": "이름(선택)",
|
||||
"public.subNotFound": "구독을 찾을 수 없습니다.",
|
||||
"public.subOptinPending": "구독 확인을 위한 이메일이 전송되었습니다.",
|
||||
"public.subPrivateList": "비공개 리스트",
|
||||
"public.subTitle": "구독",
|
||||
"public.unsub": "구독 해지",
|
||||
"public.unsubFull": "향후 모든 이메일 구독 해지",
|
||||
"public.unsubHelp": "이 메일링 리스트 구독을 해지하시겠습니까?",
|
||||
"public.unsubTitle": "구독 해지",
|
||||
"public.unsubbedInfo": "구독 해지가 완료되었습니다.",
|
||||
"public.unsubbedTitle": "구독 해지됨",
|
||||
"public.unsubscribeTitle": "메일링 리스트 구독 해지",
|
||||
"settings.appearance.adminHelp": "관리 UI에 적용할 커스텀 CSS",
|
||||
"settings.appearance.adminName": "관리자",
|
||||
"settings.appearance.customCSS": "커스텀 CSS",
|
||||
"settings.appearance.customJS": "커스텀 JavaScript",
|
||||
"settings.appearance.name": "외관",
|
||||
"settings.appearance.publicHelp": "공개 페이지에 적용할 커스텀 CSS 및 JavaScript.",
|
||||
"settings.appearance.publicName": "공개",
|
||||
"settings.bounces.action": "동작",
|
||||
"settings.bounces.blocklist": "차단 목록",
|
||||
"settings.bounces.count": "바운스 수",
|
||||
"settings.bounces.countHelp": "구독자별 바운스 수",
|
||||
"settings.bounces.enable": "바운스 처리 활성화",
|
||||
"settings.bounces.enableForwardemail": "Forward Email 활성화",
|
||||
"settings.bounces.enableMailbox": "바운스 메일함 활성화",
|
||||
"settings.bounces.enablePostmark": "Postmark 활성화",
|
||||
"settings.bounces.enableSES": "SES 활성화",
|
||||
"settings.bounces.enableSendgrid": "SendGrid 활성화",
|
||||
"settings.bounces.enableWebhooks": "바운스 웹훅 활성화",
|
||||
"settings.bounces.enabled": "활성화됨",
|
||||
"settings.bounces.folder": "폴더",
|
||||
"settings.bounces.folderHelp": "스캔할 IMAP 폴더 이름. 예: Inbox.",
|
||||
"settings.bounces.forwardemailKey": "Forward Email 키",
|
||||
"settings.bounces.invalidScanInterval": "바운스 스캔 간격은 최소 1분이어야 합니다.",
|
||||
"settings.bounces.name": "바운스",
|
||||
"settings.bounces.none": "없음",
|
||||
"settings.bounces.postmarkPassword": "Postmark 비밀번호",
|
||||
"settings.bounces.postmarkUsername": "Postmark 사용자명",
|
||||
"settings.bounces.postmarkUsernameHelp": "Postmark에서 웹훅 기본 인증을 활성화할 수 있습니다. 여기와 Postmark 웹훅 설정에 동일한 자격증명을 입력하세요.",
|
||||
"settings.bounces.scanInterval": "스캔 간격",
|
||||
"settings.bounces.scanIntervalHelp": "바운스 메일함을 스캔하는 간격 (초: s, 분: m)",
|
||||
"settings.bounces.sendgridKey": "SendGrid 키",
|
||||
"settings.bounces.type": "유형",
|
||||
"settings.bounces.username": "사용자명",
|
||||
"settings.confirmRestart": "실행 중인 캠페인이 일시정지되었는지 확인하세요. 재시작할까요?",
|
||||
"settings.duplicateMessengerName": "중복된 메신저 이름: {name}",
|
||||
"settings.errorEncoding": "설정 인코딩 오류: {error}",
|
||||
"settings.errorNoSMTP": "최소 1개의 SMTP 블록이 활성화되어야 합니다.",
|
||||
"settings.general.adminNotifEmails": "관리자 알림 이메일",
|
||||
"settings.general.adminNotifEmailsHelp": "가져오기, 캠페인 완료, 실패 등 관리자 알림을 받을 이메일 주소를 콤마로 구분하여 입력하세요.",
|
||||
"settings.general.checkUpdates": "업데이트 확인",
|
||||
"settings.general.checkUpdatesHelp": "주기적으로 새 앱 릴리스를 확인하고 알림을 보냅니다.",
|
||||
"settings.general.enablePublicArchive": "공개 메일링 리스트 아카이브 활성화",
|
||||
"settings.general.enablePublicArchiveHelp": "공개 웹사이트에 아카이브가 활성화된 캠페인을 게시합니다.",
|
||||
"settings.general.enablePublicArchiveRSSContent": "RSS 피드에 전체 내용 표시",
|
||||
"settings.general.enablePublicArchiveRSSContentHelp": "RSS 피드에 전체 이메일 내용을 표시합니다. 비활성화 시 제목과 링크만 표시됩니다.",
|
||||
"settings.general.enablePublicSubPage": "공개 구독 페이지 활성화",
|
||||
"settings.general.enablePublicSubPageHelp": "모든 공개 리스트가 포함된 구독 페이지를 표시하여 누구나 구독할 수 있게 합니다.",
|
||||
"settings.general.faviconURL": "파비콘 URL",
|
||||
"settings.general.faviconURLHelp": "(선택) 구독 해지 페이지 등 사용자 화면에 표시할 정적 파비콘의 전체 URL",
|
||||
"settings.general.fromEmail": "기본 발신 이메일",
|
||||
"settings.general.fromEmailHelp": "캠페인 이메일 발송 시 표시할 기본 발신 이메일. 캠페인별로 변경할 수 있습니다.",
|
||||
"settings.general.language": "언어",
|
||||
"settings.general.logoURL": "로고 URL",
|
||||
"settings.general.logoURLHelp": "(선택) 구독 해지 페이지 등 사용자 화면에 표시할 정적 로고의 전체 URL",
|
||||
"settings.general.name": "일반",
|
||||
"settings.general.rootURL": "루트 URL",
|
||||
"settings.general.rootURLHelp": "설치된 공개 URL(마지막 슬래시 제외)",
|
||||
"settings.general.sendOptinConfirm": "옵트인 확인 이메일 발송",
|
||||
"settings.general.sendOptinConfirmHelp": "공개 폼을 통한 가입 또는 관리자가 추가 시 옵트인 확인 이메일을 발송합니다.",
|
||||
"settings.general.siteName": "사이트 이름",
|
||||
"settings.invalidMessengerName": "잘못된 메신저 이름.",
|
||||
"settings.mailserver.authProtocol": "인증 프로토콜",
|
||||
"settings.mailserver.host": "호스트",
|
||||
"settings.mailserver.hostHelp": "SMTP 서버 호스트 주소",
|
||||
"settings.mailserver.idleTimeout": "대기 시간 초과",
|
||||
"settings.mailserver.idleTimeoutHelp": "연결을 닫고 풀에서 제거하기 전 대기 시간 (초: s, 분: m)",
|
||||
"settings.mailserver.maxConns": "최대 연결 수",
|
||||
"settings.mailserver.maxConnsHelp": "서버에 대한 최대 동시 연결 수",
|
||||
"settings.mailserver.nameHelp": "SMTP 서버의 고유 이름(선택). email- 접두사가 필요합니다. 설정 시 캠페인에서 특정 서버를 선택할 수 있습니다. 예: email-primary-server. 영문/숫자/대시만 허용.",
|
||||
"settings.mailserver.password": "비밀번호",
|
||||
"settings.mailserver.passwordHelp": "변경하려면 입력",
|
||||
"settings.mailserver.port": "포트",
|
||||
"settings.mailserver.portHelp": "SMTP 서버 포트",
|
||||
"settings.mailserver.skipTLS": "TLS 검증 건너뛰기",
|
||||
"settings.mailserver.skipTLSHelp": "TLS 인증서의 호스트명 검증을 건너뜁니다.",
|
||||
"settings.mailserver.tls": "TLS",
|
||||
"settings.mailserver.tlsHelp": "TLS/SSL 암호화. STARTTLS가 일반적으로 사용됩니다.",
|
||||
"settings.mailserver.username": "사용자명",
|
||||
"settings.mailserver.waitTimeout": "대기 시간 초과",
|
||||
"settings.mailserver.waitTimeoutHelp": "연결을 닫고 풀에서 제거하기 전 대기 시간 (초: s, 분: m)",
|
||||
"settings.maintenance.cron": "크론 간격",
|
||||
"settings.media.provider": "제공자",
|
||||
"settings.media.s3.bucket": "버킷",
|
||||
"settings.media.s3.bucketPath": "버킷 경로",
|
||||
"settings.media.s3.bucketPathHelp": "파일을 업로드할 버킷 내 경로입니다. 기본값은 / 입니다.",
|
||||
"settings.media.s3.bucketType": "버킷 유형",
|
||||
"settings.media.s3.bucketTypePrivate": "비공개",
|
||||
"settings.media.s3.bucketTypePublic": "공개",
|
||||
"settings.media.s3.key": "AWS 액세스 키",
|
||||
"settings.media.s3.publicURL": "커스텀 공개 URL (선택)",
|
||||
"settings.media.s3.publicURLHelp": "기본 S3 백엔드 URL 대신 이미지 링크에 사용할 커스텀 S3 도메인.",
|
||||
"settings.media.s3.region": "리전",
|
||||
"settings.media.s3.secret": "AWS 액세스 시크릿",
|
||||
"settings.media.s3.uploadExpiry": "업로드 만료 시간",
|
||||
"settings.media.s3.uploadExpiryHelp": "(선택) 생성된 presigned URL의 만료 시간을 지정합니다. 비공개 버킷에만 적용됩니다 (초: s, 분: m, 시간: h, 일: d).",
|
||||
"settings.media.s3.url": "S3 백엔드 URL",
|
||||
"settings.media.s3.urlHelp": "Minio 등 커스텀 S3 호환 백엔드를 사용할 때만 변경하세요.",
|
||||
"settings.media.title": "미디어 업로드",
|
||||
"settings.media.upload.extensions": "허용된 파일 확장자",
|
||||
"settings.media.upload.extensionsHelp": "*를 추가하면 모든 확장자를 허용합니다.",
|
||||
"settings.media.upload.path": "업로드 경로",
|
||||
"settings.media.upload.pathHelp": "미디어가 업로드될 디렉터리 경로입니다.",
|
||||
"settings.media.upload.uri": "업로드 URI",
|
||||
"settings.media.upload.uriHelp": "외부에서 접근 가능한 업로드 URI입니다. upload_path에 업로드된 미디어는 {root_url} 하위에서 공개됩니다. 예: https://listmonk.yoursite.com/uploads.",
|
||||
"settings.messengers.maxConns": "최대 동시 연결 수",
|
||||
"settings.messengers.maxConnsHelp": "서버에 대한 최대 동시 연결 수입니다.",
|
||||
"settings.messengers.messageSaved": "설정이 저장되었습니다. 앱을 다시 불러오는 중 ...",
|
||||
"settings.messengers.name": "메신저",
|
||||
"settings.messengers.nameHelp": "예: my-sms. 영문/숫자/대시만 허용.",
|
||||
"settings.messengers.password": "비밀번호",
|
||||
"settings.messengers.retries": "재시도 횟수",
|
||||
"settings.messengers.retriesHelp": "메시지 전송 실패 시 재시도할 횟수입니다.",
|
||||
"settings.messengers.skipTLSHelp": "TLS 인증서의 호스트명 검증을 건너뜁니다.",
|
||||
"settings.messengers.timeout": "대기 시간 초과",
|
||||
"settings.messengers.timeoutHelp": "연결을 닫고 풀에서 제거하기 전 대기 시간 (초: s, 분: m)",
|
||||
"settings.messengers.url": "URL",
|
||||
"settings.messengers.urlHelp": "Postback 서버의 루트 URL입니다.",
|
||||
"settings.messengers.username": "사용자명",
|
||||
"settings.needsRestart": "설정이 변경되었습니다. 모든 실행 중인 캠페인을 일시정지하고 앱을 재시작하세요.",
|
||||
"settings.performance.batchSize": "배치 크기",
|
||||
"settings.performance.batchSizeHelp": "한 번에 데이터베이스에서 가져올 구독자 수입니다. 각 반복마다 구독자를 가져와 메시지를 전송한 뒤 다음 배치를 가져옵니다. 이 값은 이상적으로 (동시성 * 메시지 속도)보다 커야 합니다.",
|
||||
"settings.performance.cacheSlowQueries": "느린 쿼리 캐시",
|
||||
"settings.performance.cacheSlowQueriesHelp": "대용량 데이터베이스에서만 활성화하세요. 리스트 구독자 수, 대시보드 통계 등 일부 정보를 캐시합니다.",
|
||||
"settings.performance.concurrency": "동시성",
|
||||
"settings.performance.concurrencyHelp": "동시에 메시지 전송을 시도할 최대 워커(스레드) 수입니다.",
|
||||
"settings.performance.maxErrThreshold": "최대 오류 허용치",
|
||||
"settings.performance.maxErrThresholdHelp": "실행 중인 캠페인이 수용할 수 있는 최대 오류(예: 이메일 전송 중 SMTP 타임아웃) 수입니다. 0으로 설정하면 일시정지되지 않습니다.",
|
||||
"settings.performance.messageRate": "메시지 속도",
|
||||
"settings.performance.messageRateHelp": "각 워커가 초당 전송할 수 있는 최대 메시지 수입니다. 예: 동시성=10, 메시지 속도=10이면 초당 최대 100개 메시지 전송. 서버의 제한에 맞게 조정하세요.",
|
||||
"settings.performance.name": "성능",
|
||||
"settings.performance.slidingWindow": "슬라이딩 윈도우 제한 활성화",
|
||||
"settings.performance.slidingWindowDuration": "기간",
|
||||
"settings.performance.slidingWindowDurationHelp": "슬라이딩 윈도우 기간 (분: m, 시간: h)",
|
||||
"settings.performance.slidingWindowHelp": "지정된 기간 내 전송할 수 있는 메시지 총량을 제한합니다. 한도에 도달하면 기간이 끝날 때까지 메시지 전송이 보류됩니다.",
|
||||
"settings.performance.slidingWindowRate": "최대 메시지 수",
|
||||
"settings.performance.slidingWindowRateHelp": "윈도우 기간 내 전송할 수 있는 최대 메시지 수입니다.",
|
||||
"settings.privacy.allowBlocklist": "차단 목록 허용",
|
||||
"settings.privacy.allowBlocklistHelp": "구독자가 모든 메일링 리스트에서 구독 해지 및 차단 목록 등록을 허용할지 여부",
|
||||
"settings.privacy.allowExport": "데이터 내보내기 허용",
|
||||
"settings.privacy.allowExportHelp": "구독자가 본인에 대한 데이터를 내보낼 수 있도록 허용",
|
||||
"settings.privacy.allowPrefs": "환경설정 변경 허용",
|
||||
"settings.privacy.allowPrefsHelp": "구독자가 이름, 다중 리스트 구독 등 환경설정을 변경할 수 있도록 허용",
|
||||
"settings.privacy.allowWipe": "데이터 삭제 허용",
|
||||
"settings.privacy.allowWipeHelp": "구독자가 본인 및 모든 구독 데이터를 영구적으로 삭제할 수 있도록 허용. 캠페인 조회/클릭 기록도 삭제되나 통계에는 영향 없음.",
|
||||
"settings.privacy.domainAllowlist": "도메인 허용 목록",
|
||||
"settings.privacy.domainAllowlistHelp": "이 도메인의 이메일 주소만 구독할 수 있습니다. 한 줄에 하나씩 입력. 예: example.com, *.example.com",
|
||||
"settings.privacy.domainBlocklist": "도메인 차단 목록",
|
||||
"settings.privacy.domainBlocklistHelp": "이 도메인의 이메일 주소는 구독할 수 없습니다. 한 줄에 하나씩 입력. 예: example.com",
|
||||
"settings.privacy.individualSubTracking": "개별 구독자 추적",
|
||||
"settings.privacy.individualSubTrackingHelp": "구독자별 캠페인 조회 및 클릭을 추적합니다. 비활성화 시 개별 구독자와 연결되지 않은 채로 추적됩니다.",
|
||||
"settings.privacy.listUnsubHeader": "`List-Unsubscribe` 헤더 포함",
|
||||
"settings.privacy.listUnsubHeaderHelp": "이메일 클라이언트에서 한 번의 클릭으로 구독 해지할 수 있도록 해더를 포함합니다.",
|
||||
"settings.privacy.name": "개인정보",
|
||||
"settings.privacy.recordOptinIP": "옵트인 IP 기록",
|
||||
"settings.privacy.recordOptinIPHelp": "더블 옵트인 시 구독자 속성에 IP 주소를 기록합니다.",
|
||||
"settings.restart": "재시작",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "사용자 자동 생성",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "계정이 없으면 첫 로그인 시 자동으로 사용자 생성.",
|
||||
"settings.security.OIDCClientID": "클라이언트 ID",
|
||||
"settings.security.OIDCClientSecret": "클라이언트 시크릿",
|
||||
"settings.security.OIDCDefaultListRole": "기본 리스트 역할",
|
||||
"settings.security.OIDCDefaultRoleHelp": "OIDC에서 자동 생성된 사용자에게 할당되는 기본 역할.",
|
||||
"settings.security.OIDCDefaultUserRole": "기본 사용자 역할",
|
||||
"settings.security.OIDCHelp": "OAuth 제공자를 통한 OpenID Connect OAuth2 로그인을 활성화합니다.",
|
||||
"settings.security.OIDCName": "제공자 이름",
|
||||
"settings.security.OIDCRedirectURL": "OAuth 제공자 리디렉션 URL",
|
||||
"settings.security.OIDCRedirectWarning": "프로덕션 URL이 아닌 것 같습니다. '일반' 설정에서 루트 URL을 변경하세요.",
|
||||
"settings.security.OIDCURL": "제공자 URL",
|
||||
"settings.security.OIDCWarning": "OIDC가 활성화되면 기본 비밀번호 로그인이 비활성화됩니다. 잘못된 설정 시 접근이 불가할 수 있습니다.",
|
||||
"settings.security.altchaComplexity": "Altcha 복잡도",
|
||||
"settings.security.altchaComplexityHelp": "값이 높을수록 보안은 강화되나 해결 속도는 느려집니다 (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com 사이트키",
|
||||
"settings.security.captchaKeyHelp": "www.hcaptcha.com에서 키와 시크릿을 발급받으세요.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com 시크릿",
|
||||
"settings.security.enableCaptcha": "CAPTCHA 활성화",
|
||||
"settings.security.enableCaptchaHelp": "공개 구독 폼에 CAPTCHA를 활성화합니다.",
|
||||
"settings.security.enableOIDC": "OIDC SSO 활성화",
|
||||
"settings.security.name": "보안",
|
||||
"settings.smtp.customHeaders": "커스텀 헤더",
|
||||
"settings.smtp.customHeadersHelp": "이 서버에서 발송되는 모든 메시지에 포함할 이메일 헤더 배열 (선택). 예: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"settings.smtp.enabled": "활성화됨",
|
||||
"settings.smtp.heloHost": "HELO 호스트명",
|
||||
"settings.smtp.heloHostHelp": "일부 SMTP 서버는 호스트명에 FQDN이 필요합니다. 기본값은 `localhost`입니다. 커스텀 호스트명이 필요할 때만 설정하세요.",
|
||||
"settings.smtp.name": "SMTP",
|
||||
"settings.smtp.retries": "재시도 횟수",
|
||||
"settings.smtp.retriesHelp": "메시지 전송 실패 시 재시도할 횟수입니다.",
|
||||
"settings.smtp.sendTest": "테스트 이메일 발송",
|
||||
"settings.smtp.setCustomHeaders": "커스텀 헤더 설정",
|
||||
"settings.smtp.testConnection": "연결 테스트",
|
||||
"settings.smtp.testEnterEmail": "테스트를 위해 비밀번호를 다시 입력하세요",
|
||||
"settings.smtp.toEmail": "수신 이메일",
|
||||
"settings.title": "설정",
|
||||
"settings.updateAvailable": "새 업데이트 {version}이(가) 있습니다.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "고급",
|
||||
"subscribers.advancedQueryHelp": "구독자 속성을 쿼리할 부분 SQL 표현식",
|
||||
"subscribers.attribs": "속성",
|
||||
"subscribers.attribsHelp": "속성은 JSON 맵으로 정의됩니다. 예:",
|
||||
"subscribers.blocklistedHelp": "차단된 구독자는 이메일을 절대 받지 않습니다.",
|
||||
"subscribers.confirmBlocklist": "{num}명의 구독자를 차단 목록에 추가하시겠습니까?",
|
||||
"subscribers.confirmDelete": "{num}명의 구독자를 삭제하시겠습니까?",
|
||||
"subscribers.confirmExport": "{num}명의 구독자를 내보내시겠습니까?",
|
||||
"subscribers.domainBlocklisted": "이메일 도메인이 차단 목록에 있습니다.",
|
||||
"subscribers.downloadData": "데이터 다운로드",
|
||||
"subscribers.email": "이메일",
|
||||
"subscribers.emailExists": "이미 존재하는 이메일입니다.",
|
||||
"subscribers.errorBlocklisting": "구독자 차단 오류: {error}",
|
||||
"subscribers.errorNoIDs": "ID가 제공되지 않았습니다.",
|
||||
"subscribers.errorNoListsGiven": "리스트가 제공되지 않았습니다.",
|
||||
"subscribers.errorPreparingQuery": "구독자 쿼리 준비 오류: {error}",
|
||||
"subscribers.errorSendingOptin": "옵트인 이메일 전송 오류.",
|
||||
"subscribers.export": "내보내기",
|
||||
"subscribers.invalidAction": "잘못된 동작입니다.",
|
||||
"subscribers.invalidEmail": "잘못된 이메일입니다.",
|
||||
"subscribers.invalidJSON": "속성에 잘못된 JSON이 있습니다.",
|
||||
"subscribers.invalidName": "잘못된 이름입니다.",
|
||||
"subscribers.listChangeApplied": "리스트 변경이 적용되었습니다.",
|
||||
"subscribers.lists": "리스트",
|
||||
"subscribers.listsHelp": "구독자가 직접 구독 해지한 리스트는 제거할 수 없습니다.",
|
||||
"subscribers.listsPlaceholder": "구독할 리스트",
|
||||
"subscribers.manageLists": "리스트 관리",
|
||||
"subscribers.markUnsubscribed": "구독 해지로 표시",
|
||||
"subscribers.newSubscriber": "새 구독자",
|
||||
"subscribers.numSelected": "{num}명 선택됨",
|
||||
"subscribers.optinSubject": "구독 확인",
|
||||
"subscribers.preconfirm": "사전 구독 확인",
|
||||
"subscribers.preconfirmHelp": "옵트인 이메일을 보내지 않고 모든 리스트 구독을 '구독됨'으로 표시합니다.",
|
||||
"subscribers.query": "쿼리",
|
||||
"subscribers.queryPlaceholder": "이메일 또는 이름",
|
||||
"subscribers.reset": "초기화",
|
||||
"subscribers.selectAll": "{num}명 전체 선택",
|
||||
"subscribers.sendOptinConfirm": "옵트인 확인 이메일 발송",
|
||||
"subscribers.sentOptinConfirm": "옵트인 확인 이메일 발송됨",
|
||||
"subscribers.status.blocklisted": "차단됨",
|
||||
"subscribers.status.confirmed": "확인됨",
|
||||
"subscribers.status.enabled": "활성화됨",
|
||||
"subscribers.status.subscribed": "구독됨",
|
||||
"subscribers.status.unconfirmed": "미확인",
|
||||
"subscribers.status.unsubscribed": "구독 해지됨",
|
||||
"subscribers.subscribersDeleted": "{num}명의 구독자가 삭제됨",
|
||||
"templates.cantDeleteDefault": "존재하지 않거나 기본 템플릿은 삭제할 수 없습니다.",
|
||||
"templates.default": "기본값",
|
||||
"templates.dummyName": "더미 캠페인",
|
||||
"templates.dummySubject": "더미 캠페인 제목",
|
||||
"templates.errorCompiling": "템플릿 컴파일 오류: {error}",
|
||||
"templates.errorRendering": "메시지 렌더링 오류: {error}",
|
||||
"templates.fieldInvalidName": "이름의 길이가 잘못되었습니다.",
|
||||
"templates.makeDefault": "기본값으로 설정",
|
||||
"templates.newTemplate": "새 템플릿",
|
||||
"templates.placeholderHelp": "플레이스홀더 {placeholder}는 템플릿에 정확히 한 번만 나타나야 합니다.",
|
||||
"templates.preview": "미리보기",
|
||||
"templates.rawHTML": "원본 HTML",
|
||||
"templates.subject": "제목",
|
||||
"templates.typeCampaignHTML": "캠페인 / HTML",
|
||||
"templates.typeCampaignVisual": "캠페인 / 비주얼",
|
||||
"templates.typeTransactional": "트랜잭션",
|
||||
"users.apiOneTimeToken": "API 액세스 토큰을 지금 복사하세요. 다시 표시되지 않습니다.",
|
||||
"users.cantDeleteRole": "사용 중인 역할은 삭제할 수 없습니다.",
|
||||
"users.firstTime": "최초 설치입니다. 슈퍼 관리자 계정의 사용자명과 비밀번호를 설정하세요.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "잘못된 로그인 또는 비밀번호",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "잘못된 인증 요청",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "마지막 로그인",
|
||||
"users.listPerms": "리스트 권한",
|
||||
"users.listPermsWarning": "lists:get_all 또는 lists:manage_all이 활성화되어 있으면 리스트별 권한이 무시됩니다.",
|
||||
"users.listRole": "리스트 역할",
|
||||
"users.listRoles": "리스트 역할",
|
||||
"users.login": "로그인",
|
||||
"users.loginOIDC": "{name}으로 로그인",
|
||||
"users.logout": "로그아웃",
|
||||
"users.needSuper": "업데이트할 수 없는 사용자입니다. 최소 1명의 활성 슈퍼 관리자가 필요합니다.",
|
||||
"users.newListRole": "새 리스트 역할",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "새 사용자",
|
||||
"users.newUserRole": "새 사용자 역할",
|
||||
"users.password": "비밀번호",
|
||||
"users.passwordEnable": "비밀번호 로그인 활성화",
|
||||
"users.passwordMismatch": "비밀번호가 일치하지 않습니다.",
|
||||
"users.passwordRepeat": "비밀번호 재입력",
|
||||
"users.perms": "권한",
|
||||
"users.profile": "프로필",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "역할",
|
||||
"users.roleGroup": "그룹",
|
||||
"users.roles": "역할",
|
||||
"users.status.disabled": "비활성화됨",
|
||||
"users.status.enabled": "활성화됨",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "유형",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "슈퍼 관리자",
|
||||
"users.type.user": "일반 사용자",
|
||||
"users.userRole": "사용자 역할",
|
||||
"users.userRoles": "사용자 역할",
|
||||
"users.username": "사용자명",
|
||||
"users.usernameHelp": "비밀번호 로그인에 사용됩니다."
|
||||
}
|
||||
34
i18n/ml.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "അനാഥർ",
|
||||
"email.data.info": "ജേസൺ ഫയൽ ഫോർമാറ്റിലുള്ള പ്രമാണത്തിന്റെ പകർപ്പ് ഇതിനോടൊപ്പം ചേർകക്കുന്നു. ടെക്സ്റ്റ് എഡിറ്ററുപയോഗിച്ച് കാണാനാകും.",
|
||||
"email.data.title": "നിങ്ങളുടെ വിവരങ്ങള്",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "വരിക്കാരനാകുന്നത് സ്ഥിരീകരിക്കുക",
|
||||
"email.optin.confirmSubHelp": "നിങ്ങൾ വരിക്കാരനാകുന്നത് താഴെയുള്ള ബട്ടണിൽ ഞെക്കിക്കൊണ്ട് സ്ഥിരീകരിക്കുക.",
|
||||
"email.optin.confirmSubInfo": "നിങ്ങൾ താഴെപ്പറയുന്ന ലിസ്റ്റുകളിൽ അംഗമാണ്:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "പകർത്തുക",
|
||||
"globals.buttons.delete": "നീക്കം ചെയ്യുക",
|
||||
"globals.buttons.deleteAll": "എല്ലാം നീക്കം ചെയ്യുക",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "തിരുത്തുക",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "പ്രവർത്തനക്ഷമാക്കി",
|
||||
"globals.buttons.insert": "ഉൾച്ചേർക്കുക",
|
||||
"globals.buttons.learnMore": "കൂടുതൽ അറിയുക",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "തെറ്റായ ഫീല്ഡുകള്: {name}",
|
||||
"globals.messages.invalidID": "ഐഡി അസാധുവാണ്",
|
||||
"globals.messages.invalidUUID": "യുയുഐഡി അസാധുവാണ്",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "വിട്ടുപോയ ഫീൽഡ്(കൾ): {name}",
|
||||
"globals.messages.notFound": "{name} കണ്ടെത്തിയില്ല",
|
||||
"globals.messages.numSelected": "{num} തിരഞ്ഞെടുക്കപ്പെട്ടത്",
|
||||
"globals.messages.passwordChange": "മാറ്റം വരുത്തേണ്ട വില രേഖപ്പെടുത്തുക",
|
||||
"globals.messages.passwordChangeFull": "'{name}' എന്നില് നിന്ന് പൂര്ണ്ണമായി പാസ്വേഡ് മാറ്റുക.",
|
||||
"globals.messages.permissionDenied": "അനുമതി നിഷേധിച്ചു: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "ടെംപ്ലേറ്റ് | ടെംപ്ലേറ്റുകൾ",
|
||||
"globals.terms.templates": "ടെംപ്ലേറ്റുകൾ",
|
||||
"globals.terms.tx": "ഇടപാട് | ഇടപാട്",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "ഉപയോക്താവ് | ഉപയോക്താക്കള്",
|
||||
"globals.terms.users": "ഉപയോക്താക്കള്",
|
||||
"globals.terms.year": "വർഷം | വർഷങ്ങൾ",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "പിൻതുണക്കാത്ത തരം ഫയൽ({type})",
|
||||
"media.upload": "അപ്ലോഡ്",
|
||||
"media.uploadHelp": "ഒന്നോ അതിലധികമോ ചിത്രങ്ങൾ വലിച്ചിട്ടോ അമർത്തിയോ ഇവിടെ കൊണ്ടുവരിക",
|
||||
"media.uploadImage": "ചിത്രം അപ്ലോഡ് ചെയ്യുക",
|
||||
"menu.allCampaigns": "എല്ലാ ക്യാമ്പേയ്നുകളും",
|
||||
"menu.allLists": "എല്ലാ ലിസ്റ്റുകളും",
|
||||
"menu.allSubscribers": "എല്ലാ വരിക്കാരും",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "ഓപ്റ്റ്-ഇന് IP വിലാസം രേഖപ്പെടുത്തൂ",
|
||||
"settings.privacy.recordOptinIPHelp": "ഡബിള് ഓപ്റ്റ് ഇന്സ് സബ്സ്ക്രൈബറുടെ വിവരഗണനയിലേക്ക് IP വിലാസം രേഖപ്പെടുത്തൂ.",
|
||||
"settings.restart": "പുനരാരംഭിയ്ക്കുക",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "ഉപയോക്താക്കൾ സ്വയം സൃഷ്ടിക്കുക",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "അക്കൗണ്ട് നിലവിലില്ലെങ്കിൽ ആദ്യ ലോഗിനിൽ സ്വയം ഉപയോക്താവിനെ സൃഷ്ടിക്കും.",
|
||||
"settings.security.OIDCClientID": "ക്ലയന്റ് ഐഡി",
|
||||
"settings.security.OIDCClientSecret": "ക്ലയന്റ് സീക്രട്ട്",
|
||||
"settings.security.OIDCDefaultListRole": "ഡിഫോൾട്ട് ലിസ്റ്റ് റോളുകൾ",
|
||||
"settings.security.OIDCDefaultRoleHelp": "OIDC-യിൽ നിന്നുള്ള സ്വയം സൃഷ്ടിക്കപ്പെട്ട ഉപയോക്താക്കൾക്ക് നൽകപ്പെടുന്ന ഡിഫോൾട്ട് റോളുകൾ.",
|
||||
"settings.security.OIDCDefaultUserRole": "ഡിഫോൾട്ട് ഉപയോക്തൃ റോളുകൾ",
|
||||
"settings.security.OIDCHelp": "ഒപ്പെന്ഐഡി കണക്റ്റ് ഓഴോത്ത്_2 ലോഗിന് ഒഎആത്വര്ഗ്ഗത്തിന് ഒഎഓപി പ്രേഷകനമാക്കുക.",
|
||||
"settings.security.OIDCName": "പ്രൊവൈഡർ പേര്",
|
||||
"settings.security.OIDCRedirectURL": "ഓഓആത്വര്ഗ്ഗത്തിലേക്കായ മാലിന്റ്ആര്ട്ടിയിലേക്ക് തിരിയുക",
|
||||
"settings.security.OIDCRedirectWarning": "ഇത് സഞ്ചാരം URL ഈഴവഴിക്കുന്നതിനായാണ് തോന്നുന്നത്. ''പൊതുവോക്ക്'' അമൂല്യമായ മൂല URL മാറ്റൂ.",
|
||||
"settings.security.OIDCURL": "പ്രേഷകനമായ URL",
|
||||
"settings.security.OIDCWarning": "ഓ ഐ ഡി സജ്ജീകരിച്ചാല്, സ്ഥിരതയായ പാസ്വേഡ് ലോഗിന് അസാധുവാക്കപ്പെടുമെന്നാണ്. അസാധുവായ വിന്യാസം നിങ്ങളെ അടിമകളാക്കാന് പ്രതിഫലിപ്പിക്കും.",
|
||||
"settings.security.altchaComplexity": "Altcha സങ്കീർണ്ണത",
|
||||
"settings.security.altchaComplexityHelp": "കൂടുതൽ വിലകൾ മികച്ച സുരക്ഷ നൽകുന്നു, പക്ഷേ പരിഹരിക്കൽ മന്ദഗതിയിലാണ് (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com സൈറ്റ്കീ",
|
||||
"settings.security.captchaKeyHelp": "കീ ലഭിക്കാൻ www.hcaptcha.com സന്ദര്ശിക്കുക.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com രഹസ്യം",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "അയക്കുന്ന ഇ-മെയിൽ വിലാസം",
|
||||
"settings.title": "ക്രമീകരണങ്ങൾ",
|
||||
"settings.updateAvailable": "ഒരു പുതിയ അപ്ഡേറ്റ് {version} ലഭ്യമാണ്.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "വിപുലമായത്",
|
||||
"subscribers.advancedQueryHelp": "വരിക്കാരുടെ വിവരങ്ങൾ മനസിലാക്കുന്നതിനായുള്ള ഭാഗികമായ SQL പ്രയേഗം",
|
||||
"subscribers.attribs": "ആട്രിബ്യൂട്ടുകൾ",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "അപി പ്രവേശ ടോക്കനെ ഇപ്പോള് പകർത്തൂ. അത് പുതുവും കാണപ്പെടാനില്ല.",
|
||||
"users.cantDeleteRole": "ഉപയോക്താവ് ഉപയോഗത്തിലാക്കിയ പങ്ക് ഒഴിവാക്കാനാവില്ല.",
|
||||
"users.firstTime": "ഇത് പുതിയതായി ഇൻസ്റ്റാള് ചെയ്ത ആകൗശലം അകൗണെഡ്ജ് ഉപയോക്താവായിരിക്കുന്നു. സൂപ്പർ അഡ്മിൻ അക്കൗണ്ടിന് ഉപയോഗിക്കുകയും പാസ്വേഡ് തിരഞ്ഞെടുക്കുകയും ചെയ്യുക.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "അസാധുവായ ലോഗിന് അല്ലെങ്കിൽ പാസ്വേഡ്",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "അസാധുവായ പ്രവൃത്തിയുള്ള അനുമതിയുണ്ട്",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "അവസാന ലോഗിന്",
|
||||
"users.listPerms": "പട്ടിക അനുമതികള്",
|
||||
"users.listPermsWarning": "എല്ലാവരുടെയും യാത്രൊപികള്:മാര്പ്പുകള്:മാര്പ്പുകള്പ്പൂര്ണമാക്കുന്നുവെന്നുള്ളതാണ് പ്രവര്ത്തിപ്പിക്കുന്നത്",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "പുറത്തുകടക്കുക",
|
||||
"users.needSuper": "ഉപയോക്താക്കളെ(s) അപ്ഡേറ്റ് ചെയ്യുന്നതിന് കഴിയില്ല. അതിനായാണ് അത്യാവശ്യമായി അക്കൗണ്ടുകളിൽ കുറവ് ഒരു സൂപ്പർ അഡ്മിൻ ഉണ്ടായിരിക്കേണ്ടത്.",
|
||||
"users.newListRole": "പുതിയ പട്ടികപ്രവർത്തനം",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "പുതിയ ഉപയോക്താവ്",
|
||||
"users.newUserRole": "പുതിയ ഉപയോക്താവ് പങ്ക്",
|
||||
"users.password": "പാസ്വേഡ്",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "പാസ്വേഡ് മാറ്റുക",
|
||||
"users.perms": "അനുമതികൾ",
|
||||
"users.profile": "പ്രൊഫൈല്",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "പങ്ക് | പങ്കുകള്",
|
||||
"users.roleGroup": "ഗ്രൂപ്പ്",
|
||||
"users.roles": "പങ്കുകള്",
|
||||
"users.status.disabled": "അപ്രാപ്തമാക്കി",
|
||||
"users.status.enabled": "സജീവമാക്കി",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "തരം",
|
||||
"users.type.api": "എപിഐ",
|
||||
"users.type.super": "സൂപ്പർ അഡ്മിൻ",
|
||||
|
||||
36
i18n/nl.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Wezen",
|
||||
"email.data.info": "In bijlage vindt u een kopie van alle data verzameld over u in JSON formaat. Het kan beken worden met een tekstverwerkingsprogramma.",
|
||||
"email.data.title": "Uw data",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Bevestig inschrijving",
|
||||
"email.optin.confirmSubHelp": "Bevestig uw inschrijving door op onderstaande knop te klikken.",
|
||||
"email.optin.confirmSubInfo": "U bent aan volgende lijsten toegevoegd:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopiëren",
|
||||
"globals.buttons.delete": "Verwijder",
|
||||
"globals.buttons.deleteAll": "Verwijder alles",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Bewerken",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Ingeschakeld",
|
||||
"globals.buttons.insert": "Invoegen",
|
||||
"globals.buttons.learnMore": "Meer leren",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Ongeldige velden: {name}",
|
||||
"globals.messages.invalidID": "Ongeldige ID(s)",
|
||||
"globals.messages.invalidUUID": "Ongeldige UUID(s)",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Ontbrekend(e) veld(en): {name}",
|
||||
"globals.messages.notFound": "{name} niet gevonden",
|
||||
"globals.messages.numSelected": "{num} geselecteerd",
|
||||
"globals.messages.passwordChange": "Geef een nieuw wachtwoord in",
|
||||
"globals.messages.passwordChangeFull": "Wis en voer het volledige wachtwoord opnieuw in bij '{name}'.",
|
||||
"globals.messages.permissionDenied": "Toegang geweigerd: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Sjabloon | Sjablonen",
|
||||
"globals.terms.templates": "Sjablonen",
|
||||
"globals.terms.tx": "Transactioneel | Transactionele",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Gebruiker | Gebruikers",
|
||||
"globals.terms.users": "Gebruikers",
|
||||
"globals.terms.year": "Jaar | Jaren",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Bestandstype niet ondersteund ({type})",
|
||||
"media.upload": "Opladen",
|
||||
"media.uploadHelp": "Klik of sleep een of meer afbeeldingen naar hier",
|
||||
"media.uploadImage": "Afbeelding opladen",
|
||||
"menu.allCampaigns": "Alle campagnes",
|
||||
"menu.allLists": "Alle lijsten",
|
||||
"menu.allSubscribers": "Alle abonnees",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Opt-in IP-adres registreren",
|
||||
"settings.privacy.recordOptinIPHelp": "IP-adres van dubbele opt-ins registreren bij abonnee-attributen.",
|
||||
"settings.restart": "Herstarten",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Gebruikers automatisch aanmaken",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Maak automatisch een gebruiker aan bij de eerste login als het account nog niet bestaat.",
|
||||
"settings.security.OIDCClientID": "Client ID",
|
||||
"settings.security.OIDCClientSecret": "Clientgeheim",
|
||||
"settings.security.OIDCDefaultListRole": "Standaard lijstrol",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Standaardrol toegewezen aan gebruikers die automatisch worden aangemaakt via OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Standaard gebruikersrol",
|
||||
"settings.security.OIDCHelp": "Schakel inloggen via OpenID Connect OAuth2 in via een OAuth-provider.",
|
||||
"settings.security.OIDCName": "Provider naam",
|
||||
"settings.security.OIDCRedirectURL": "Redirect-URL voor oAuth-provider",
|
||||
"settings.security.OIDCRedirectWarning": "Dit lijkt geen productie-URL te zijn. Wijzig de Root-URL in de 'Algemene' instellingen.",
|
||||
"settings.security.OIDCURL": "Provider-URL",
|
||||
"settings.security.OIDCWarning": "Als OIDC is ingeschakeld, is de standaardwachtwoordlogin uitgeschakeld. Ongeldige configuratie kan u buitensluiten.",
|
||||
"settings.security.altchaComplexity": "Altcha-complexiteit",
|
||||
"settings.security.altchaComplexityHelp": "Hogere waarden zorgen voor betere beveiliging maar vertragen het oplossen (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com SiteKey",
|
||||
"settings.security.captchaKeyHelp": "Ga naar www.hcaptcha.com om de sleutel en het geheim te verkrijgen.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com-geheim",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Naar e-mail",
|
||||
"settings.title": "Instellingen",
|
||||
"settings.updateAvailable": "Een nieuwe update {version} is beschikbaar.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Geavanceerd",
|
||||
"subscribers.advancedQueryHelp": "Gedeeltelijke SQL uitdrukking om abonnees attributen op te vragen",
|
||||
"subscribers.attribs": "Attributen",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Kopieer nu de API-toegangstoken. Deze wordt niet opnieuw weergegeven.",
|
||||
"users.cantDeleteRole": "Kan geen rol verwijderen die in gebruik is.",
|
||||
"users.firstTime": "Dit is een nieuwe installatie. Kies een gebruikersnaam en wachtwoord voor het Super Admin-account.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Ongeldige inloggegevens",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Ongeldig verzoek voor verificatie",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Laatste login",
|
||||
"users.listPerms": "Lijstmachtigingen",
|
||||
"users.listPermsWarning": "Lijst:get_all of lijst:beheren_all zijn ingeschakeld, waardoor de per-lijst machtigingen worden overschreven",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Uitloggen",
|
||||
"users.needSuper": "Gebruiker(s) konden niet worden bijgewerkt. Er moet altijd minstens één actieve Super Admin zijn.",
|
||||
"users.newListRole": "Nieuwe lijstrol",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nieuwe gebruiker",
|
||||
"users.newUserRole": "Nieuwe gebruikersrol",
|
||||
"users.password": "Wachtwoord",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Bevestig wachtwoord",
|
||||
"users.perms": "Rechten",
|
||||
"users.profile": "Profiel",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rol | Rollen",
|
||||
"users.roleGroup": "Groep",
|
||||
"users.roles": "Rollen",
|
||||
"users.status.disabled": "Uitgeschakeld",
|
||||
"users.status.enabled": "Ingeschakeld",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Type",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
@@ -651,4 +683,4 @@
|
||||
"users.userRoles": "Gebruikersrollen",
|
||||
"users.username": "Gebruikersnaam",
|
||||
"users.usernameHelp": "Wordt gebruikt voor inloggen met een wachtwoord"
|
||||
}
|
||||
}
|
||||
|
||||
34
i18n/no.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Foreldreløse abonnenter",
|
||||
"email.data.info": "En kopi av all data registrert på deg er vedlagt som en fil i JSON-format. Den kan vises i en teksteditor.",
|
||||
"email.data.title": "Dine data",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Bekreft abonnement",
|
||||
"email.optin.confirmSubHelp": "Bekreft abonnementet ditt ved å klikke på knappen nedenfor.",
|
||||
"email.optin.confirmSubInfo": "Du har blitt lagt til følgende lister:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopier",
|
||||
"globals.buttons.delete": "Slett",
|
||||
"globals.buttons.deleteAll": "Slett alt",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Rediger",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Aktivert",
|
||||
"globals.buttons.insert": "Sett inn",
|
||||
"globals.buttons.learnMore": "Lær mer",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Ugyldige felt: {name}",
|
||||
"globals.messages.invalidID": "Ugyldig(e) ID-er",
|
||||
"globals.messages.invalidUUID": "Ugyldig(e) UUID-er",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Mangler felt(er): {name}",
|
||||
"globals.messages.notFound": "{name} ikke funnet",
|
||||
"globals.messages.numSelected": "{num} valgt",
|
||||
"globals.messages.passwordChange": "Skriv inn en verdi for å endre",
|
||||
"globals.messages.passwordChangeFull": "Tøm og skriv inn hele passordet på nytt i '{name}'.",
|
||||
"globals.messages.permissionDenied": "Tillatelse nektet: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Mal | Maler",
|
||||
"globals.terms.templates": "Maler",
|
||||
"globals.terms.tx": "Transaksjonell | Transaksjonell",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Bruker | Brukere",
|
||||
"globals.terms.users": "Brukere",
|
||||
"globals.terms.year": "År | År",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Ikke-støttet filtype ({type})",
|
||||
"media.upload": "Last opp",
|
||||
"media.uploadHelp": "Klikk eller dra ett eller flere bilder hit",
|
||||
"media.uploadImage": "Last opp bilde",
|
||||
"menu.allCampaigns": "Alle kampanjer",
|
||||
"menu.allLists": "Alle lister",
|
||||
"menu.allSubscribers": "Alle abonnenter",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Registrer opt-in IP-adresse",
|
||||
"settings.privacy.recordOptinIPHelp": "Registrer IP-adressen for dobbelt opt-ins i abonnentattributtene.",
|
||||
"settings.restart": "Start på nytt",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Opprett brukere automatisk",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Opprett automatisk bruker ved første pålogging hvis kontoen ikke eksisterer.",
|
||||
"settings.security.OIDCClientID": "Klient-ID",
|
||||
"settings.security.OIDCClientSecret": "Klienthemmelighet",
|
||||
"settings.security.OIDCDefaultListRole": "Standard listerolle",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Standardrolle tildelt brukere som opprettes automatisk fra OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Standard brukerrolle",
|
||||
"settings.security.OIDCHelp": "Aktiver OpenID Connect OAuth2-pålogging via en OAuth-leverandør.",
|
||||
"settings.security.OIDCName": "Leverandørnavn",
|
||||
"settings.security.OIDCRedirectURL": "Omdirigerings-URL for OAuth-leverandør",
|
||||
"settings.security.OIDCRedirectWarning": "Dette ser ikke ut til å være en produksjons-URL. Endre Rot-URL i 'Generelle' innstillinger.",
|
||||
"settings.security.OIDCURL": "Leverandør-URL",
|
||||
"settings.security.OIDCWarning": "Når OIDC er aktivert, deaktiveres standard passordinnlogging. Ugyldig konfigurasjon kan låse deg ute.",
|
||||
"settings.security.altchaComplexity": "Altcha-kompleksitet",
|
||||
"settings.security.altchaComplexityHelp": "Høyere verdier gir bedre sikkerhet, men tregere løsning (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com SiteKey",
|
||||
"settings.security.captchaKeyHelp": "Besøk www.hcaptcha.com for å få nøkkelen og hemmeligheten.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com hemmelighet",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Til e-post",
|
||||
"settings.title": "Innstillinger",
|
||||
"settings.updateAvailable": "En ny oppdatering {version} er tilgjengelig.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avansert",
|
||||
"subscribers.advancedQueryHelp": "Delvis SQL-uttrykk for å søke i abonnentattributter",
|
||||
"subscribers.attribs": "Attributter",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Kopier API-tilgangstokenet nå. Det vil ikke bli vist igjen.",
|
||||
"users.cantDeleteRole": "Kan ikke slette rolle som er i bruk.",
|
||||
"users.firstTime": "Dette er en fersk installasjon. Velg et brukernavn og passord for Super Admin-kontoen.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Ugyldig innlogging eller passord",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Ugyldig autentiseringsforespørsel",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Siste innlogging",
|
||||
"users.listPerms": "Listerettigheter",
|
||||
"users.listPermsWarning": "lists:get_all eller lists:manage_all er aktivert, noe som overstyrer per-liste tillatelser",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Logg ut",
|
||||
"users.needSuper": "Bruker(e) kunne ikke oppdateres. Det må være minst én aktiv Super Admin-bruker.",
|
||||
"users.newListRole": "Ny listrolle",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Ny bruker",
|
||||
"users.newUserRole": "Ny brukerrolle",
|
||||
"users.password": "Passord",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Gjenta passord",
|
||||
"users.perms": "Tillatelser",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rolle | Roller",
|
||||
"users.roleGroup": "Gruppe",
|
||||
"users.roles": "Roller",
|
||||
"users.status.disabled": "Deaktivert",
|
||||
"users.status.enabled": "Aktivert",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Type",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Superadmin",
|
||||
|
||||
34
i18n/pl.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Porzucone",
|
||||
"email.data.info": "Kopia wszystkich zarejestrowanych danych o Tobie jest dołączona jako plik w formacie JSON. Może zostać otworzona w edytorze tekstu.",
|
||||
"email.data.title": "Twoje dane",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Potwierdź subskrypcję",
|
||||
"email.optin.confirmSubHelp": "Potwierdź subskrypcję naciskając przycisk poniżej.",
|
||||
"email.optin.confirmSubInfo": "Zostałeś dodany(a) do następujących list:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopiuj",
|
||||
"globals.buttons.delete": "Usuń",
|
||||
"globals.buttons.deleteAll": "Usuń wszystkie",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Edytuj",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Włączone",
|
||||
"globals.buttons.insert": "Wstaw",
|
||||
"globals.buttons.learnMore": "Dowiedz się więcej",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Nieprawidłowe pola: {name}",
|
||||
"globals.messages.invalidID": "Nieprawidłowy ID",
|
||||
"globals.messages.invalidUUID": "Nieprawidłowy UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Brakujące pole(a): {name}",
|
||||
"globals.messages.notFound": "{name} nie znaleziono",
|
||||
"globals.messages.numSelected": "{num} wybrano",
|
||||
"globals.messages.passwordChange": "Podaj wartość do zmiany",
|
||||
"globals.messages.passwordChangeFull": "Wyczyść i ponownie wprowadź pełne hasło w '{name}'.",
|
||||
"globals.messages.permissionDenied": "Brak uprawnień: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Szablon | Szablony",
|
||||
"globals.terms.templates": "Szablony",
|
||||
"globals.terms.tx": "Transakcyjne | Transakcyjne",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Użytkownik | Użytkownicy",
|
||||
"globals.terms.users": "Użytkownicy",
|
||||
"globals.terms.year": "Rok | Lat",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Niewspierany typ pliku ({type})",
|
||||
"media.upload": "Wysyłanie",
|
||||
"media.uploadHelp": "Kliknij lub przeciągnij jeden lub więcej plików tutaj",
|
||||
"media.uploadImage": "Wyślij obraz",
|
||||
"menu.allCampaigns": "Wszystkie kampanie",
|
||||
"menu.allLists": "Wszystkie listy",
|
||||
"menu.allSubscribers": "Wszyscy subskrybenci",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Zapisz adres IP zgody na otrzymywanie",
|
||||
"settings.privacy.recordOptinIPHelp": "Zapisz adres IP podwójnej zgody na otrzymywanie w atrybutach subskrybenta.",
|
||||
"settings.restart": "Uruchom ponownie",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Automatyczne tworzenie użytkowników",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Automatycznie tworzy użytkownika przy pierwszym logowaniu, jeśli konto nie istnieje.",
|
||||
"settings.security.OIDCClientID": "ID klienta",
|
||||
"settings.security.OIDCClientSecret": "Sekret klienta",
|
||||
"settings.security.OIDCDefaultListRole": "Domyślna rola na liście",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Domyślna rola przypisana użytkownikom automatycznie tworzonym z OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Domyślna rola użytkownika",
|
||||
"settings.security.OIDCHelp": "Włącz logowanie OAuth2 za pomocą OpenID Connect OAuth2 za pomocą dostawcy OAuth.",
|
||||
"settings.security.OIDCName": "Nazwa dostawcy",
|
||||
"settings.security.OIDCRedirectURL": "URL przekierowania dla dostawcy oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Wygląda na to, że to nie jest adres URL produkcyjny. Zmień adres URL root w ustawieniach „Ogólne”.",
|
||||
"settings.security.OIDCURL": "Adres URL dostawcy",
|
||||
"settings.security.OIDCWarning": "Po włączeniu OIDC, logowanie domyślnie za pomocą hasła jest wyłączone. Nieprawidłowa konfiguracja może zablokować dostęp.",
|
||||
"settings.security.altchaComplexity": "Złożoność Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Wyższe wartości zapewniają lepsze bezpieczeństwo, ale wolniejsze rozwiązywanie (1000-1000000).",
|
||||
"settings.security.captchaKey": "Klucz witryny hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Wejdź na www.hcaptcha.com w celu pobrania klucza i sekretu.",
|
||||
"settings.security.captchaSecret": "Tajny klucz witryny hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Adres e-mail odbiorcy",
|
||||
"settings.title": "Ustawienia",
|
||||
"settings.updateAvailable": "Nowa wersja {version} jest dostępna.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Zaawansowane",
|
||||
"subscribers.advancedQueryHelp": "Częściowe zapytania SQL w celu pobrania atrybutów subskrybentów",
|
||||
"subscribers.attribs": "Atrybuty",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Skopiuj teraz token dostępu API. Nie zostanie ponownie wyświetlony.",
|
||||
"users.cantDeleteRole": "Nie można usunąć roli, która jest w użyciu.",
|
||||
"users.firstTime": "To jest nowa instalacja. Wybierz nazwę użytkownika i hasło dla konta Super Admina.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Nieprawidłowe dane logowania lub hasło",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Nieprawidłowe żądanie uwierzytelniania",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Ostatnie logowanie",
|
||||
"users.listPerms": "Uprawnienia listy",
|
||||
"users.listPermsWarning": "Włączone są uprawnienia lists:get_all lub lists:manage_all, co przesłoni uprawnienia na poziomie listy",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Wyloguj",
|
||||
"users.needSuper": "Nie można zaktualizować użytkowników. Musi istnieć co najmniej jedno aktywne konto Super Admina.",
|
||||
"users.newListRole": "Nowa rola listy",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nowy użytkownik",
|
||||
"users.newUserRole": "Nowa rola użytkownika",
|
||||
"users.password": "Hasło",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Powtórz hasło",
|
||||
"users.perms": "Uprawnienia",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rola | Role",
|
||||
"users.roleGroup": "Grupa",
|
||||
"users.roles": "Role",
|
||||
"users.status.disabled": "Wyłączone",
|
||||
"users.status.enabled": "Włączone",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Typ",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Órfãos",
|
||||
"email.data.info": "Uma cópia de todos os dados associados a você está anexado em um arquivo JSON. Ele pode ser ler o conteúdo em um editor de texto.",
|
||||
"email.data.title": "Seus dados",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirmar a assinatura",
|
||||
"email.optin.confirmSubHelp": "Confirme sua assinatura clicando no botão abaixo.",
|
||||
"email.optin.confirmSubInfo": "Você foi adicionado às seguintes listas:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copiar",
|
||||
"globals.buttons.delete": "Excluir",
|
||||
"globals.buttons.deleteAll": "Apagar tudo",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Editar",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Habilitado",
|
||||
"globals.buttons.insert": "Inserir",
|
||||
"globals.buttons.learnMore": "Saiba mais",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Campos inválidos: {name}",
|
||||
"globals.messages.invalidID": "ID inválido",
|
||||
"globals.messages.invalidUUID": "UUID inválido",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Campos ausente(s): {name}",
|
||||
"globals.messages.notFound": "{name} não encontrado",
|
||||
"globals.messages.numSelected": "{num} selecionado(s)",
|
||||
"globals.messages.passwordChange": "Digite um valor para alterar",
|
||||
"globals.messages.passwordChangeFull": "Limpe e insira novamente a senha completa em '{name}'.",
|
||||
"globals.messages.permissionDenied": "Permissão negada: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Modelo | Modelos",
|
||||
"globals.terms.templates": "Modelos",
|
||||
"globals.terms.tx": "Transacional | Transacionais",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Usuário | Usuários",
|
||||
"globals.terms.users": "Usuários",
|
||||
"globals.terms.year": "Ano | Anos",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Tipo de arquivo não suportado ({type})",
|
||||
"media.upload": "Enviar arquivo",
|
||||
"media.uploadHelp": "Clique ou arraste uma ou mais imagens aqui",
|
||||
"media.uploadImage": "Enviar Imagem",
|
||||
"menu.allCampaigns": "Todas as campanhas",
|
||||
"menu.allLists": "Todas as listas",
|
||||
"menu.allSubscribers": "Todos os inscritos",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Registrar endereço IP de aceitação",
|
||||
"settings.privacy.recordOptinIPHelp": "Registrar o endereço IP de aceitação dupla nas atributos do assinante.",
|
||||
"settings.restart": "Reiniciar",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Criar usuários automaticamente",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Cria automaticamente o usuário no primeiro login se a conta não existir.",
|
||||
"settings.security.OIDCClientID": "ID do cliente",
|
||||
"settings.security.OIDCClientSecret": "Segredo do cliente",
|
||||
"settings.security.OIDCDefaultListRole": "Função padrão da lista",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Função padrão atribuída aos usuários criados automaticamente via OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Função padrão do usuário",
|
||||
"settings.security.OIDCHelp": "Permite o login OpenID Connect OAuth2 através de um provedor OAuth.",
|
||||
"settings.security.OIDCName": "Nome do provedor",
|
||||
"settings.security.OIDCRedirectURL": "URL de redirecionamento para o provedor oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Esta parece não ser uma URL de produção. Altere a URL raiz nas configurações 'Geral'.",
|
||||
"settings.security.OIDCURL": "URL do provedor",
|
||||
"settings.security.OIDCWarning": "Quando o OIDC está habilitado, o login padrão por senha é desativado. Configurações inválidas podem te deixar bloqueado.",
|
||||
"settings.security.altchaComplexity": "Complexidade do Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Valores maiores oferecem melhor segurança, porém a resolução fica mais lenta (1000-1000000).",
|
||||
"settings.security.captchaKey": "Chave do Site hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Visite www.hcaptcha.com para obter a chave e o segredo.",
|
||||
"settings.security.captchaSecret": "Segredo do Site hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "E-mail para",
|
||||
"settings.title": "Configurações",
|
||||
"settings.updateAvailable": "Atualização: a nova versão {version} já está disponível.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avançado",
|
||||
"subscribers.advancedQueryHelp": "Expressão de SQL parcial para consultar atributos dos inscritos",
|
||||
"subscribers.attribs": "Atributos",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copie o token de acesso à API agora. Ele não será mostrado novamente.",
|
||||
"users.cantDeleteRole": "Não é possível excluir um papel que está em uso.",
|
||||
"users.firstTime": "Esta é uma instalação nova. Escolha um nome de usuário e uma senha para a conta de Super Administrador.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Nome de usuário ou senha inválidos",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Requisição de autenticação inválida",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Último login",
|
||||
"users.listPerms": "Permissões de lista",
|
||||
"users.listPermsWarning": "as permissões lists:get_all ou lists:manage_all estão habilitadas, o que substitui as permissões específicas de cada lista",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Sair",
|
||||
"users.needSuper": "O(s) usuário(s) não pode(m) ser atualizado(s). Deve haver pelo menos um usuário Super Administrador ativo.",
|
||||
"users.newListRole": "Novo papel da lista",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Novo usuário",
|
||||
"users.newUserRole": "Novo papel do usuário",
|
||||
"users.password": "Senha",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Repetir senha",
|
||||
"users.perms": "Permissões",
|
||||
"users.profile": "Perfil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Papel | Papéis",
|
||||
"users.roleGroup": "Grupo",
|
||||
"users.roles": "Papéis",
|
||||
"users.status.disabled": "Desabilitado",
|
||||
"users.status.enabled": "Habilitado",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tipo",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/pt.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Órfãos",
|
||||
"email.data.info": "Uma cópia de todos os seus dados está em anexo em formato JSON. Pode ser visualizada num editor de texto.",
|
||||
"email.data.title": "Os seus dados",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirmar subscrição",
|
||||
"email.optin.confirmSubHelp": "Confirme a sua subscrição clicando no botão abaixo.",
|
||||
"email.optin.confirmSubInfo": "Foi adicionado às seguintes listas:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copiar",
|
||||
"globals.buttons.delete": "Eliminar",
|
||||
"globals.buttons.deleteAll": "Eliminar todos",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Editar",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Ativo",
|
||||
"globals.buttons.insert": "Inserir",
|
||||
"globals.buttons.learnMore": "Saber mais",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Campos inválidos: {name}",
|
||||
"globals.messages.invalidID": "ID inválido",
|
||||
"globals.messages.invalidUUID": "UUID inválido",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Campo(s) em falta: {name}",
|
||||
"globals.messages.notFound": "{name} não encontrado",
|
||||
"globals.messages.numSelected": "{num} selecionado(s)",
|
||||
"globals.messages.passwordChange": "Insere um valor para alterar",
|
||||
"globals.messages.passwordChangeFull": "Limpe e digite novamente a senha completa em '{name}'.",
|
||||
"globals.messages.permissionDenied": "Permissão negada: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Modelo | Modelos",
|
||||
"globals.terms.templates": "Modelo",
|
||||
"globals.terms.tx": "Transacional | Transacional",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Usuário | Usuários",
|
||||
"globals.terms.users": "Usuários",
|
||||
"globals.terms.year": "Ano | Anos",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Tipo de ficheiro não suportado ({type})",
|
||||
"media.upload": "Carregar",
|
||||
"media.uploadHelp": "Clica ou arrasta uma ou mais imagens aqui",
|
||||
"media.uploadImage": "Enviar imagens",
|
||||
"menu.allCampaigns": "Todas as campanhas",
|
||||
"menu.allLists": "Todas as listas",
|
||||
"menu.allSubscribers": "Todos os subscritores",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Registrar endereço de IP de opt-in",
|
||||
"settings.privacy.recordOptinIPHelp": "Registrar o endereço IP de opt-ins duplos nos atributos do assinante.",
|
||||
"settings.restart": "Reiniciar",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Criar usuários automaticamente",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Criar usuário automaticamente no primeiro login se a conta não existir.",
|
||||
"settings.security.OIDCClientID": "ID do Cliente",
|
||||
"settings.security.OIDCClientSecret": "Segredo do Cliente",
|
||||
"settings.security.OIDCDefaultListRole": "Perfil padrão da lista",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Perfil padrão atribuído a usuários criados automaticamente via OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Perfil padrão do usuário",
|
||||
"settings.security.OIDCHelp": "Habilitar login OAuth2 do OpenID Connect via um fornecedor OAuth.",
|
||||
"settings.security.OIDCName": "Nome do provedor",
|
||||
"settings.security.OIDCRedirectURL": "URL de redirecionamento para o provedor de oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Isto não parece ser uma URL de produção. Altere a URL Raiz nas configurações 'Gerais'.",
|
||||
"settings.security.OIDCURL": "URL do Provedor",
|
||||
"settings.security.OIDCWarning": "Quando o OIDC está habilitado, o login de senha padrão é desabilitado. Configuração inválida pode bloqueá-lo.",
|
||||
"settings.security.altchaComplexity": "Complexidade do Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Valores mais altos fornecem melhor segurança, mas resolução mais lenta (1000-1000000).",
|
||||
"settings.security.captchaKey": "Chave do SiteKey do hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Visite www.hcaptcha.com para obter a chave e o segredo.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com segredo",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "E-mail do destinatário",
|
||||
"settings.title": "Definições",
|
||||
"settings.updateAvailable": "A nova versão {version} está disponível.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avançado",
|
||||
"subscribers.advancedQueryHelp": "Expressão SQL parcial para consultar atributos de subscritores",
|
||||
"subscribers.attribs": "Atributos",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copie agora o token de acesso à API. Ele não será mostrado novamente.",
|
||||
"users.cantDeleteRole": "Não é possível excluir a função que está sendo utilizada.",
|
||||
"users.firstTime": "Esta é uma nova instalação. Escolha um nome de usuário e senha para a conta de Super Administrador.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Login ou senha inválidos",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Requisição de autenticação inválida",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Último login",
|
||||
"users.listPerms": "Permissões de lista",
|
||||
"users.listPermsWarning": "lists:get_all ou lists:manage_all estão habilitados, o que sobrescreve as permissões por lista",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Sair",
|
||||
"users.needSuper": "Usuário(s) não puderam ser atualizados. Deve haver pelo menos um usuário Super Administrador ativo.",
|
||||
"users.newListRole": "Nova função de lista",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Novo usuário",
|
||||
"users.newUserRole": "Nova função do usuário",
|
||||
"users.password": "Senha",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Repetir senha",
|
||||
"users.perms": "Permissões",
|
||||
"users.profile": "Perfil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Função | Funções",
|
||||
"users.roleGroup": "Grupo",
|
||||
"users.roles": "Funções",
|
||||
"users.status.disabled": "Desabilitado",
|
||||
"users.status.enabled": "Habilitado",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tipo",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Administrador",
|
||||
|
||||
34
i18n/ro.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Orfani",
|
||||
"email.data.info": "O copie a tuturor datelor înregistrate pe tine este atașată ca fișier în format JSON. Acesta poate fi vizualizat într-un editor de text.",
|
||||
"email.data.title": "Datele tale",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Confirmați abonamentul",
|
||||
"email.optin.confirmSubHelp": "Confirmați-vă abonamentul făcând clic pe butonul de mai jos.",
|
||||
"email.optin.confirmSubInfo": "Ați fost adăugat la următoarele liste:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Copiază",
|
||||
"globals.buttons.delete": "Şterge",
|
||||
"globals.buttons.deleteAll": "Șterge tot",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Editare",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Activat",
|
||||
"globals.buttons.insert": "Introduceți",
|
||||
"globals.buttons.learnMore": "Află mai mult",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Câmpuri nevalide: {name}",
|
||||
"globals.messages.invalidID": "ID de hub nevalid",
|
||||
"globals.messages.invalidUUID": "UUID nevalid",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Câmp(e) lipsă: {name}",
|
||||
"globals.messages.notFound": "{name} nu a fost găsit",
|
||||
"globals.messages.numSelected": "{num} selectate",
|
||||
"globals.messages.passwordChange": "Introducerea unei valori de modificat",
|
||||
"globals.messages.passwordChangeFull": "Ștergeți și reintroduceți parola completă în '{name}'.",
|
||||
"globals.messages.permissionDenied": "Acces interzis: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Șabloane WhatsApp",
|
||||
"globals.terms.templates": "Șabloane",
|
||||
"globals.terms.tx": "Tranzacțional | Tranzacțional",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Utilizator | Utilizatori",
|
||||
"globals.terms.users": "Utilizatori",
|
||||
"globals.terms.year": "Anul",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Tip de fișier neacceptat ({type})",
|
||||
"media.upload": "Încarcă",
|
||||
"media.uploadHelp": "Click sau trage una sau mai multe imagini aici",
|
||||
"media.uploadImage": "Încarcă imaginea",
|
||||
"menu.allCampaigns": "Toate campaniile",
|
||||
"menu.allLists": "Toate listele",
|
||||
"menu.allSubscribers": "Toți abonații",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Înregistrare adresă IP de opt-in",
|
||||
"settings.privacy.recordOptinIPHelp": "Înregistrați adresa IP a confirmărilor duble în atributele abonaților.",
|
||||
"settings.restart": "Repornește",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Creare automată a utilizatorilor",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Creează automat utilizatorul la prima autentificare dacă contul nu există.",
|
||||
"settings.security.OIDCClientID": "ID client",
|
||||
"settings.security.OIDCClientSecret": "Secret client",
|
||||
"settings.security.OIDCDefaultListRole": "Rol listă implicit",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Rolul implicit asignat utilizatorilor creați automat din OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Rol utilizator implicit",
|
||||
"settings.security.OIDCHelp": "Activează autentificarea OpenID Connect OAuth2 prin intermediul unui furnizor OAuth.",
|
||||
"settings.security.OIDCName": "Numele furnizorului",
|
||||
"settings.security.OIDCRedirectURL": "URL de redirecționare pentru furnizorul oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Aceasta nu pare a fi o adresă URL de producție. Modificați URL-ul de bază în setăriile 'Generale'.",
|
||||
"settings.security.OIDCURL": "URL furnizor",
|
||||
"settings.security.OIDCWarning": "Când OIDC este activat, autentificarea implicită cu parolă este dezactivată. Configurarea incorectă poate duce la blocarea accesului.",
|
||||
"settings.security.altchaComplexity": "Complexitatea Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Valorile mai mari oferă o securitate mai bună, dar rezolvarea este mai lentă (1000-1000000).",
|
||||
"settings.security.captchaKey": "Cheie SiteKey hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Vizitați www.hcaptcha.com pentru a obține cheia și secretul.",
|
||||
"settings.security.captchaSecret": "Secret hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Pentru a e-mail",
|
||||
"settings.title": "Setări",
|
||||
"settings.updateAvailable": "Este disponibilă o nouă actualizare {version}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avansat",
|
||||
"subscribers.advancedQueryHelp": "Expresie SQL parțială pentru a interoga atributele abonatului",
|
||||
"subscribers.attribs": "Atribute",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Copiați acum tokenul de acces API. Nu va fi afișat din nou.",
|
||||
"users.cantDeleteRole": "Imposibil de șters rolul care este în uz.",
|
||||
"users.firstTime": "Aceasta este o instalare nouă. Alegeți un nume de utilizator și o parolă pentru contul Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Autentificare sau parolă incorectă",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Cerere de autentificare nevalidă",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Ultima autentificare",
|
||||
"users.listPerms": "Permisiuni listă",
|
||||
"users.listPermsWarning": "lists:get_all sau lists:manage_all sunt activate, ceea ce suprascrie permisiunile pe listă",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Deconectare",
|
||||
"users.needSuper": "Utilizator(izatorii) nu au putut fi actualizați. Trebuie să existe cel puțin un utilizator Super Admin activ.",
|
||||
"users.newListRole": "Rol listă nou",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Utilizator nou",
|
||||
"users.newUserRole": "Rol utilizator nou",
|
||||
"users.password": "Parolă",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Repetă parola",
|
||||
"users.perms": "Permisiuni",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rol | Roluri",
|
||||
"users.roleGroup": "Grup",
|
||||
"users.roles": "Roluri",
|
||||
"users.status.disabled": "Dezactivat",
|
||||
"users.status.enabled": "Activat",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tip",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/ru.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Без списков",
|
||||
"email.data.info": "Копия всех записанных данных о вас прилагается в виде файла в формате JSON. Его можно просмотреть в текстовом редакторе.",
|
||||
"email.data.title": "Ваши данные",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Подтвердить подписку",
|
||||
"email.optin.confirmSubHelp": "Подтвердите подписку, нажав кнопку ниже.",
|
||||
"email.optin.confirmSubInfo": "Вы были добавлены в следующие списки:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Копировать",
|
||||
"globals.buttons.delete": "Удалить",
|
||||
"globals.buttons.deleteAll": "Удалить всё",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Редактировать",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Включено",
|
||||
"globals.buttons.insert": "Вставить",
|
||||
"globals.buttons.learnMore": "Подробнее",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Некорректные поля: {name}",
|
||||
"globals.messages.invalidID": "Неверный ID",
|
||||
"globals.messages.invalidUUID": "Неверный UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Отсутствует поле (поля): {name}",
|
||||
"globals.messages.notFound": "{name} не найдено",
|
||||
"globals.messages.numSelected": "{num} выбрано",
|
||||
"globals.messages.passwordChange": "Введите значение для изменения",
|
||||
"globals.messages.passwordChangeFull": "Очистите и повторно введите полный пароль в поле '{name}'.",
|
||||
"globals.messages.permissionDenied": "Доступ запрещён: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Шаблон | Шаблоны",
|
||||
"globals.terms.templates": "Шаблоны",
|
||||
"globals.terms.tx": "Транзакционный | Транзакционные",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Пользователь | Пользователи",
|
||||
"globals.terms.users": "Пользователи",
|
||||
"globals.terms.year": "Год | Годы",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Неподдерживаемый тип файла ({type})",
|
||||
"media.upload": "Загрузить",
|
||||
"media.uploadHelp": "Нажмите или перетащите сюда одно или несколько изображений",
|
||||
"media.uploadImage": "Загрузить изображение",
|
||||
"menu.allCampaigns": "Все кампании",
|
||||
"menu.allLists": "Все списки",
|
||||
"menu.allSubscribers": "Все подписчики",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Записывать IP-адрес подтверждения подписки",
|
||||
"settings.privacy.recordOptinIPHelp": "Записывать IP-адрес двойных подтверждений в атрибуты подписчика.",
|
||||
"settings.restart": "Перезапустить",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Автоматическое создание пользователей",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Автоматически создаёт пользователя при первом входе, если аккаунт не существует.",
|
||||
"settings.security.OIDCClientID": "ID клиента",
|
||||
"settings.security.OIDCClientSecret": "Секрет клиента",
|
||||
"settings.security.OIDCDefaultListRole": "Роль по умолчанию для списка",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Роль по умолчанию, назначаемая пользователям, автоматически созданным из OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Роль пользователя по умолчанию",
|
||||
"settings.security.OIDCHelp": "Включить вход через OpenID Connect OAuth2 через провайдера OAuth.",
|
||||
"settings.security.OIDCName": "Имя провайдера",
|
||||
"settings.security.OIDCRedirectURL": "URL перенаправления для провайдера OAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Это не похоже на производственный URL. Измените корневой URL в настройках 'Общие'.",
|
||||
"settings.security.OIDCURL": "URL провайдера",
|
||||
"settings.security.OIDCWarning": "При включении OIDC вход по паролю по умолчанию отключается. Неверная конфигурация может заблокировать доступ.",
|
||||
"settings.security.altchaComplexity": "Сложность Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Более высокие значения обеспечивают лучшую безопасность, но более медленное решение (1000-1000000).",
|
||||
"settings.security.captchaKey": "Ключ сайта hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Посетите www.hcaptcha.com, чтобы получить ключ и секрет.",
|
||||
"settings.security.captchaSecret": "Секрет hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Кому (электронная почта)",
|
||||
"settings.title": "Настройки",
|
||||
"settings.updateAvailable": "Доступно новое обновление {version}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Расширенный",
|
||||
"subscribers.advancedQueryHelp": "Частичное SQL-выражение для запроса атрибутов подписчиков",
|
||||
"subscribers.attribs": "Атрибуты",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Скопируйте токен доступа API сейчас. Он больше не будет показан.",
|
||||
"users.cantDeleteRole": "Невозможно удалить роль, которая используется.",
|
||||
"users.firstTime": "Это новая установка. Выберите имя пользователя и пароль для учётной записи Супер Админа.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Неверный логин или пароль",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Неверный запрос аутентификации",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Последний вход",
|
||||
"users.listPerms": "Список разрешений",
|
||||
"users.listPermsWarning": "Включены lists:get_all или lists:manage_all, что переопределяет разрешения для отдельных списков",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Выйти",
|
||||
"users.needSuper": "Пользователь(и) не могут быть обновлены. Должен быть хотя бы один активный пользователь Супер Админа.",
|
||||
"users.newListRole": "Новая роль списка",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Новый пользователь",
|
||||
"users.newUserRole": "Новая роль пользователя",
|
||||
"users.password": "Пароль",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Повторите пароль",
|
||||
"users.perms": "Разрешения",
|
||||
"users.profile": "Профиль",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Роль | Роли",
|
||||
"users.roleGroup": "Группа",
|
||||
"users.roles": "Роли",
|
||||
"users.status.disabled": "Отключён",
|
||||
"users.status.enabled": "Включён",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Тип",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Супер Админ",
|
||||
|
||||
34
i18n/se.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Föräldralösa",
|
||||
"email.data.info": "En kopia av all data som registrerats om dig bifogas som en fil i JSON-format. Det kan visas i en textredigerare.",
|
||||
"email.data.title": "Din data",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Bekräfta prenumeration",
|
||||
"email.optin.confirmSubHelp": "Bekräfta din prenumeration genom att klicka på knappen nedan.",
|
||||
"email.optin.confirmSubInfo": "Du har lagts till följande listor:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopiera",
|
||||
"globals.buttons.delete": "Ta bort",
|
||||
"globals.buttons.deleteAll": "Ta bort allt",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Redigera",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Aktiverat",
|
||||
"globals.buttons.insert": "Infoga",
|
||||
"globals.buttons.learnMore": "Läs mer",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Ogiltiga fält: {name}",
|
||||
"globals.messages.invalidID": "Ogiltigt ID/ID:er",
|
||||
"globals.messages.invalidUUID": "Ogiltigt UUID/UUID:n",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Saknade fält: {name}",
|
||||
"globals.messages.notFound": "{name} hittades inte",
|
||||
"globals.messages.numSelected": "{num} valda",
|
||||
"globals.messages.passwordChange": "Ange ett värde för att ändra",
|
||||
"globals.messages.passwordChangeFull": "Rensa och ange hela lösenordet i '{name}'.",
|
||||
"globals.messages.permissionDenied": "Åtkomst nekad: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Mall | Mallar",
|
||||
"globals.terms.templates": "Mallar",
|
||||
"globals.terms.tx": "Transaktion | Transaktioner",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Användare | Användare",
|
||||
"globals.terms.users": "Användare",
|
||||
"globals.terms.year": "År | År",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Ogiltig filtyp ({type})",
|
||||
"media.upload": "Ladda upp",
|
||||
"media.uploadHelp": "Klicka eller dra hit en eller flera bilder",
|
||||
"media.uploadImage": "Ladda upp bild",
|
||||
"menu.allCampaigns": "Alla kampanjer",
|
||||
"menu.allLists": "Alla listor",
|
||||
"menu.allSubscribers": "Alla prenumeranter",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Registrera opt-in-IP-adress",
|
||||
"settings.privacy.recordOptinIPHelp": "Registrera IP-adress för dubbelopt-in i prenumerationars attribut.",
|
||||
"settings.restart": "Starta om",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Skapa användare automatiskt",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Skapa automatiskt användare vid första inloggning om kontot inte finns.",
|
||||
"settings.security.OIDCClientID": "Klient-ID",
|
||||
"settings.security.OIDCClientSecret": "Klienthemlighet",
|
||||
"settings.security.OIDCDefaultListRole": "Standardlistroll",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Standardroll tilldelad användare som automatiskt skapas från OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Standardanvändarroll",
|
||||
"settings.security.OIDCHelp": "Aktivera inloggning med OpenID Connect OAuth2 via en OAuth-leverantör.",
|
||||
"settings.security.OIDCName": "Leverantörsnamn",
|
||||
"settings.security.OIDCRedirectURL": "URL för omdirigering för oAuth-leverantör",
|
||||
"settings.security.OIDCRedirectWarning": "Det här verkar inte vara en produktions-URL. Ändra roten-URL i 'Allmänt' inställningar.",
|
||||
"settings.security.OIDCURL": "Leverantörs-URL",
|
||||
"settings.security.OIDCWarning": "När OIDC är aktiverat är standardlösenordsinloggning inaktiverad. Ogiltig konfiguration kan låsa dig ute.",
|
||||
"settings.security.altchaComplexity": "Altcha-komplexitet",
|
||||
"settings.security.altchaComplexityHelp": "Högre värden ger bättre säkerhet men långsammare lösning (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com SiteKey",
|
||||
"settings.security.captchaKeyHelp": "Besök www.hcaptcha.com för att få nyckeln och hemligheten.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com hemlighet",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Till e-post",
|
||||
"settings.title": "Inställningar",
|
||||
"settings.updateAvailable": "En ny uppdatering {version} finns tillgänglig.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Avancerad",
|
||||
"subscribers.advancedQueryHelp": "Del SQL-uttryck för att fråga prenumerantattribut",
|
||||
"subscribers.attribs": "Attribut",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Kopiera nu API-åtkomstoken. Det visas inte igen.",
|
||||
"users.cantDeleteRole": "Det går inte att ta bort en användarroll som används.",
|
||||
"users.firstTime": "Det här är en nyinstallation. Välj ett användarnamn och lösenord för användarkontot för superadmin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Ogiltig inloggning eller lösenord",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Ogiltig autentiseringförfrågan",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Senast inloggad",
|
||||
"users.listPerms": "Listbehörigheter",
|
||||
"users.listPermsWarning": "lists:get_all eller lists:manage_all är aktiverade vilket upphäver per-listupplevelser",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Logga ut",
|
||||
"users.needSuper": "Användare kan inte uppdateras. Det måste finnas minst en aktiv superadmin-användare.",
|
||||
"users.newListRole": "Ny lista roll",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Ny användare",
|
||||
"users.newUserRole": "Ny användarroll",
|
||||
"users.password": "Lösenord",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Upprepa lösenord",
|
||||
"users.perms": "Behörigheter",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Roll | Roller",
|
||||
"users.roleGroup": "Grupp",
|
||||
"users.roles": "Roller",
|
||||
"users.status.disabled": "Avaktiverad",
|
||||
"users.status.enabled": "Aktiverad",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Typ",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/sk.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Siroty",
|
||||
"email.data.info": "Kópia všetkých údajov, ktoré sme uložili, je pripojená ako súbor vo formáte JSON. Dá sa zobraziť v textovom editore.",
|
||||
"email.data.title": "Vaše údaje",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Potvrďte odber",
|
||||
"email.optin.confirmSubHelp": "Potvrďte svoj odber kliknutím na tlačidlo nižšie.",
|
||||
"email.optin.confirmSubInfo": "Ste prihlásený do týchto zoznamov:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopírovať",
|
||||
"globals.buttons.delete": "Odstrániť",
|
||||
"globals.buttons.deleteAll": "Odstrániť všetko",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Upraviť",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Povolené",
|
||||
"globals.buttons.insert": "Vložiť",
|
||||
"globals.buttons.learnMore": "Dalšie informácie",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Neplatné polia: {name}",
|
||||
"globals.messages.invalidID": "Neplatné ID",
|
||||
"globals.messages.invalidUUID": "Neplatné UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Chýbajúce pole: {name}",
|
||||
"globals.messages.notFound": "{name} sa nenašlo",
|
||||
"globals.messages.numSelected": "{num} vybraných",
|
||||
"globals.messages.passwordChange": "Zadajte zmenenú hodnotu",
|
||||
"globals.messages.passwordChangeFull": "Zadajte celé heslo v '{name}' znova.",
|
||||
"globals.messages.permissionDenied": "Povolenie odmietnuté: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Šablóna | Šablóny",
|
||||
"globals.terms.templates": "Šablóny",
|
||||
"globals.terms.tx": "Transakčné | Transakčné",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Používateľ | Používatelia",
|
||||
"globals.terms.users": "Používatelia",
|
||||
"globals.terms.year": "Rok | Roky",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Nepodporovaný typ súboru ({type})",
|
||||
"media.upload": "Odoslať",
|
||||
"media.uploadHelp": "Klikniten alebo presuňte jeden alebo viac obrázkov sem",
|
||||
"media.uploadImage": "Odoslač obrázok",
|
||||
"menu.allCampaigns": "Všetky kampane",
|
||||
"menu.allLists": "Všetky zoznamy",
|
||||
"menu.allSubscribers": "Všetci odberatelia",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Zaznamenávať IP adresu opt-in",
|
||||
"settings.privacy.recordOptinIPHelp": "Zaznamenávať IP adresu pri dvojitej opt-in v atribútoch odberateľov.",
|
||||
"settings.restart": "Restarť",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Automaticky vytvárať používateľov",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Automaticky vytvoriť používateľa pri prvom prihlásení, ak účet neexistuje.",
|
||||
"settings.security.OIDCClientID": "ID klienta",
|
||||
"settings.security.OIDCClientSecret": "Heslo klienta",
|
||||
"settings.security.OIDCDefaultListRole": "Predvolená rola v zozname",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Predvolená rola priradená používateľom automaticky vytvoreným cez OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Predvolená používateľská rola",
|
||||
"settings.security.OIDCHelp": "Povoľuje prihlásenie sa pomocou OpenID Connect OAuth2 cez poskytovateľa OAuth.",
|
||||
"settings.security.OIDCName": "Názov poskytovateľa",
|
||||
"settings.security.OIDCRedirectURL": "Presmerovacia URL adresa pre poskytovateľa oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Toto sa nezdá byť produkčnou URL adresou. Zmeňte koreňovú URL adresu v nastaveniach „Všeobecné“.",
|
||||
"settings.security.OIDCURL": "URL poskytovateľa",
|
||||
"settings.security.OIDCWarning": "Pri zapnutom OIDC je vypnuté predvolené prihlasovanie heslom. Nevhodná konfigurácia môže vám znemožniť prístup.",
|
||||
"settings.security.altchaComplexity": "Zložitosť Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Vyššie hodnoty poskytujú lepšiu bezpečnosť, ale pomalšie riešenie (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com kľúč webovej stránky",
|
||||
"settings.security.captchaKeyHelp": "Navštívte www.hcaptcha.com, aby ste získali kľúč a tajomstvo.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com tajomstvo",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Na e-mail",
|
||||
"settings.title": "Nastavenia",
|
||||
"settings.updateAvailable": "Nová aktualizácia {version} je k dispozícii.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Rozšírené",
|
||||
"subscribers.advancedQueryHelp": "Časť výrazu SQL k dotazu na atribúty odberateľov",
|
||||
"subscribers.attribs": "Atribúty",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Skopírujte prístupový token k API. Nebude zobrazený znova.",
|
||||
"users.cantDeleteRole": "Nie je možné odstrániť rolu, ktorá sa používa.",
|
||||
"users.firstTime": "Je to čerstvá inštalácia. Vyberte si používateľské meno a heslo pre účet Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Neplatné prihlásenie alebo heslo",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Neplatná autentifikačná žiadosť",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Posledné prihlásenie",
|
||||
"users.listPerms": "Povolenia na zoznamy",
|
||||
"users.listPermsWarning": "sú povolené povolenia lists:get_all alebo lists:manage_all, ktoré prepisujú povolenia pre jednotlivé zoznamy",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Odhlásiť",
|
||||
"users.needSuper": "Používateľ(ia) sa nepodarilo aktualizovať. Musí existovať aspoň jeden aktívny používateľ s oprávneniami Super Admin.",
|
||||
"users.newListRole": "Nová rola zoznamu",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nový používateľ",
|
||||
"users.newUserRole": "Nová používateľská rola",
|
||||
"users.password": "Heslo",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Opakujte heslo",
|
||||
"users.perms": "Oprávnenia",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rola | Role",
|
||||
"users.roleGroup": "Skupina",
|
||||
"users.roles": "Role",
|
||||
"users.status.disabled": "Zablokovaný",
|
||||
"users.status.enabled": "Aktívny",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Typ",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
|
||||
34
i18n/sl.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Osirote",
|
||||
"email.data.info": "Kopija vseh podatkov, zabeleženih o vas, je priložena kot datoteka v formatu JSON. Ogledate si jo lahko v urejevalniku besedil.",
|
||||
"email.data.title": "Vaši podatki",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Potrdi naročnino",
|
||||
"email.optin.confirmSubHelp": "Potrdite svojo naročnino s klikom na spodnji gumb.",
|
||||
"email.optin.confirmSubInfo": "Dodani ste bili na naslednje sezname:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopiraj",
|
||||
"globals.buttons.delete": "Izbriši",
|
||||
"globals.buttons.deleteAll": "Izbriši vse",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Uredi",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Omogočeno",
|
||||
"globals.buttons.insert": "Vstavi",
|
||||
"globals.buttons.learnMore": "Več o tem",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Neveljavna polja: {name}",
|
||||
"globals.messages.invalidID": "Neveljavni ID(-ji)",
|
||||
"globals.messages.invalidUUID": "Neveljavni UUID(-ji)",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Manjkajoče polje(a): {name}",
|
||||
"globals.messages.notFound": "{name} ni bilo mogoče najti",
|
||||
"globals.messages.numSelected": "{num} izbranih",
|
||||
"globals.messages.passwordChange": "Vnesite vrednost za spremembo",
|
||||
"globals.messages.passwordChangeFull": "Počisti in znova vnesi celotno geslo v '{name}'.",
|
||||
"globals.messages.permissionDenied": "Dostop zavrnjen: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Predloga | Predloge",
|
||||
"globals.terms.templates": "Predloge",
|
||||
"globals.terms.tx": "Transakcijsko | Transakcijsko",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Uporabnik | Uporabnika",
|
||||
"globals.terms.users": "Uporabniki",
|
||||
"globals.terms.year": "Leto | Leta",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Nepodprta vrsta datoteke ({type})",
|
||||
"media.upload": "Naloži",
|
||||
"media.uploadHelp": "Kliknite ali povlecite eno ali več slik sem",
|
||||
"media.uploadImage": "Naloži sliko",
|
||||
"menu.allCampaigns": "Vse akcije",
|
||||
"menu.allLists": "Vsi seznami",
|
||||
"menu.allSubscribers": "Vsi naročniki",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Zabeleži IP naslov za privolitev",
|
||||
"settings.privacy.recordOptinIPHelp": "Zabeleži naslov IP dvojne privolitve v atribute naročnika.",
|
||||
"settings.restart": "Ponovni zagon",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Samodejno ustvarjanje uporabnikov",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Samodejno ustvari uporabnika ob prvem prijavljanju, če račun ne obstaja.",
|
||||
"settings.security.OIDCClientID": "ID odjemalca",
|
||||
"settings.security.OIDCClientSecret": "Skrivnost odjemalca",
|
||||
"settings.security.OIDCDefaultListRole": "Privzeta vloga na seznamu",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Privzeta vloga dodeljena uporabnikom, samodejno ustvarjenim prek OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Privzeta vloga uporabnika",
|
||||
"settings.security.OIDCHelp": "Omogoči prijavo preko OpenID Connect OAuth2 prek ponudnika OAuth.",
|
||||
"settings.security.OIDCName": "Ime ponudnika",
|
||||
"settings.security.OIDCRedirectURL": "Preusmeritveni URL za ponudnika oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "To se ne zdi proizvodni URL. Spremenite osnovni URL v nastavitvah 'Splošno'.",
|
||||
"settings.security.OIDCURL": "URL ponudnika",
|
||||
"settings.security.OIDCWarning": "Ko je OMPC omogočen, je privzeta prijava z geslom onemogočena. Neveljavna konfiguracija vas lahko zaklene.",
|
||||
"settings.security.altchaComplexity": "Kompleksnost Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Višje vrednosti zagotavljajo boljšo varnost, vendar počasnejše reševanje (1000-1000000).",
|
||||
"settings.security.captchaKey": "Ključ mestu hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Obiščite www.hcaptcha.com za pridobitev ključa in skrivnosti.",
|
||||
"settings.security.captchaSecret": "skrivnost hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Na e-pošto",
|
||||
"settings.title": "Nastavitve",
|
||||
"settings.updateAvailable": "Nova posodobitev {version} je na voljo.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Napredno",
|
||||
"subscribers.advancedQueryHelp": "Delni izraz SQL za poizvedovanje atributov naročnika",
|
||||
"subscribers.attribs": "Atributi",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Zdaj skopirajte žeton za dostop do API-ja. Ne bo več prikazan.",
|
||||
"users.cantDeleteRole": "Ne morete izbrisati vloge, ki je v uporabi.",
|
||||
"users.firstTime": "To je sveža namestitev. Izberite uporabniško ime in geslo za super upravni račun.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Neveljavna prijava ali geslo",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Neveljavna zahteva za preverjanje pristnosti",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Zadnja prijava",
|
||||
"users.listPerms": "Dovoljenja za sezname",
|
||||
"users.listPermsWarning": "omogočena sta lists:get_all ali lists:manage_all, kar prepiše dovoljenja za posamezne sezname",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Odjava",
|
||||
"users.needSuper": "Uporabnika/jev ni mogoče posodobiti. Najmanj eden od aktivnih super upravljalcev mora obstajati.",
|
||||
"users.newListRole": "Nova vloga seznama",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Nov uporabnik",
|
||||
"users.newUserRole": "Nova vloga uporabnika",
|
||||
"users.password": "Geslo",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Ponovite geslo",
|
||||
"users.perms": "Dovoljenja",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Vloga | Vloge",
|
||||
"users.roleGroup": "Skupina",
|
||||
"users.roles": "Vloge",
|
||||
"users.status.disabled": "Onemogočeno",
|
||||
"users.status.enabled": "Omogočeno",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Vrsta",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super upravljalec",
|
||||
|
||||
34
i18n/tr.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Sahipsiz",
|
||||
"email.data.info": "Hakkınızda üretilmiş tüm veri JSON formatında bir dosya olarak eklendi. Bir meti düzenleyici ile görüntüleyebilirsiniz.",
|
||||
"email.data.title": "Sizin veriniz",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Üyeliği onaylayınız",
|
||||
"email.optin.confirmSubHelp": "Aşağıdaki düğmeyi tıklayarak Üyeliği onaylayınız.",
|
||||
"email.optin.confirmSubInfo": "Buradaki listelere eklendiniz:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Kopyala",
|
||||
"globals.buttons.delete": "Sil",
|
||||
"globals.buttons.deleteAll": "Tamamını sil",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Değiştir",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Etkinleştirildi",
|
||||
"globals.buttons.insert": "Ekle",
|
||||
"globals.buttons.learnMore": "Daha fazla öğren",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Geçersiz alanlar: {name}",
|
||||
"globals.messages.invalidID": "Yanlış ID",
|
||||
"globals.messages.invalidUUID": "Yanlış UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Eksik alan(lar): {name}",
|
||||
"globals.messages.notFound": "{name} bulunamadı",
|
||||
"globals.messages.numSelected": "{num} seçildi",
|
||||
"globals.messages.passwordChange": "Değiştirmek için değer gir",
|
||||
"globals.messages.passwordChangeFull": "'{name}' içinde parolayı temizleyin ve yeniden girin.",
|
||||
"globals.messages.permissionDenied": "İzin reddedildi: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Taslak | Taslaklar",
|
||||
"globals.terms.templates": "Taslaklar",
|
||||
"globals.terms.tx": "İşlem | İşlem",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Kullanıcı | Kullanıcılar",
|
||||
"globals.terms.users": "Kullanıcılar",
|
||||
"globals.terms.year": "Yıl | Yıllar",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Desteklenmeyen dosya tipi ({type})",
|
||||
"media.upload": "Yükleme",
|
||||
"media.uploadHelp": "Bir veya daha fazla resmi buraya bırak veya tıkla",
|
||||
"media.uploadImage": "Resim yükle",
|
||||
"menu.allCampaigns": "Tüm kampanyalar",
|
||||
"menu.allLists": "Tüm listeler",
|
||||
"menu.allSubscribers": "Tüm üyeler",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Opt-in IP adresini kaydet",
|
||||
"settings.privacy.recordOptinIPHelp": "Çift onay aboneliklerinin IP adreslerini abone özelliklerinde kaydedin.",
|
||||
"settings.restart": "Yeniden başlat",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Kullanıcıları otomatik oluştur",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Hesap yoksa ilk girişte kullanıcıyı otomatik oluştur.",
|
||||
"settings.security.OIDCClientID": "İstemci Kimliği",
|
||||
"settings.security.OIDCClientSecret": "İstemci Sırrı",
|
||||
"settings.security.OIDCDefaultListRole": "Varsayılan liste rolü",
|
||||
"settings.security.OIDCDefaultRoleHelp": "OIDC'den otomatik oluşturulan kullanıcılara atanan varsayılan rol.",
|
||||
"settings.security.OIDCDefaultUserRole": "Varsayılan kullanıcı rolü",
|
||||
"settings.security.OIDCHelp": "Bir OAuth sağlayıcı aracılığıyla OpenID Connect OAuth2 girişini etkinleştirin.",
|
||||
"settings.security.OIDCName": "Sağlayıcı adı",
|
||||
"settings.security.OIDCRedirectURL": "OAuth sağlayıcı için Yönlendirme URL'si",
|
||||
"settings.security.OIDCRedirectWarning": "Bu, üretim URL'si gibi görünmüyor. 'Genel' ayarlarında Kök URL'yi değiştirin.",
|
||||
"settings.security.OIDCURL": "Sağlayıcı URL'si",
|
||||
"settings.security.OIDCWarning": "OIDC etkin olduğunda, varsayılan parola girişi devre dışı bırakılır. Geçersiz yapılandırma sizi kilitleyebilir.",
|
||||
"settings.security.altchaComplexity": "Altcha Karmaşıklığı",
|
||||
"settings.security.altchaComplexityHelp": "Daha yüksek değerler daha iyi güvenlik sağlar ancak çözüm yavaşlar (1000-1000000).",
|
||||
"settings.security.captchaKey": "hCaptcha.com Site Anahtarı",
|
||||
"settings.security.captchaKeyHelp": "Anahtarı ve gizli bilgiyi almak için www.hcaptcha.com adresini ziyaret edin.",
|
||||
"settings.security.captchaSecret": "hCaptcha.com gizli bilgi",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Gönderilecek e-posta",
|
||||
"settings.title": "Ayarlar",
|
||||
"settings.updateAvailable": "Yeni bir güncel sürüm {version} mevcuttur.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "İleri düzey",
|
||||
"subscribers.advancedQueryHelp": "Üye attributes verisini görüntülemek için SQL verisi",
|
||||
"subscribers.attribs": "Nitelikler",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Şimdi API erişim belirtecini kopyalayın. Bir daha gösterilmeyecek.",
|
||||
"users.cantDeleteRole": "Kullanımda olan bir rolü silemezsin.",
|
||||
"users.firstTime": "Bu yeni bir yüklemeler. Süper Yönetici hesabı için bir kullanıcı adı ve şifre seçin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Geçersiz giriş veya şifre",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Geçersiz kimlik doğrulama isteği",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Son giriş",
|
||||
"users.listPerms": "Liste izinleri",
|
||||
"users.listPermsWarning": "lists:get_all veya lists:manage_all etkinleştirilmiş durumdadır ve bunlar per-liste izinleri geçersiz kılar",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Çıkış",
|
||||
"users.needSuper": "Kullanıcı(lar) güncellenemedi. En az bir etkin Süper Yönetici kullanıcısı olmalıdır.",
|
||||
"users.newListRole": "Yeni liste rolü",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Yeni kullanıcı",
|
||||
"users.newUserRole": "Yeni kullanıcı rolü",
|
||||
"users.password": "Şifre",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Şifreyi Tekrarla",
|
||||
"users.perms": "İzinler",
|
||||
"users.profile": "Profil",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Rol | Roller",
|
||||
"users.roleGroup": "Grup",
|
||||
"users.roles": "Roller",
|
||||
"users.status.disabled": "Devre Dışı",
|
||||
"users.status.enabled": "Etkin",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Tür",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Süper Yönetici",
|
||||
|
||||
34
i18n/uk.json
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "Без розсилок",
|
||||
"email.data.info": "Копію всіх зібраних про вас даних вкладено як файл у форматі JSON. Можете переглянути його в текстовому редакторі.",
|
||||
"email.data.title": "Ваші дані",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Підтвердити підписку",
|
||||
"email.optin.confirmSubHelp": "Щоб підтвердити підписку, натисніть кнопку внизу.",
|
||||
"email.optin.confirmSubInfo": "Вас додано до наступних розсилок:",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Копіювати",
|
||||
"globals.buttons.delete": "Видалити",
|
||||
"globals.buttons.deleteAll": "Видалити все",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Редагувати",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Увімкнено",
|
||||
"globals.buttons.insert": "Вставити",
|
||||
"globals.buttons.learnMore": "Дізнатися більше",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "Хибні поля: {name}",
|
||||
"globals.messages.invalidID": "Хибні ідентифікатори",
|
||||
"globals.messages.invalidUUID": "Хибні UUID-коди",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Бракує полів: {name}",
|
||||
"globals.messages.notFound": "{name} не знайдено",
|
||||
"globals.messages.numSelected": "{num} вибрано",
|
||||
"globals.messages.passwordChange": "Щоб змінити, введіть нове значення",
|
||||
"globals.messages.passwordChangeFull": "Зітріть і введіть заново повний пароль у '{name}'.",
|
||||
"globals.messages.permissionDenied": "Відмова в доступі: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Шаблон | Шаблони",
|
||||
"globals.terms.templates": "Шаблони",
|
||||
"globals.terms.tx": "Транзакція | Транзакції",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Користувач | Користувачі",
|
||||
"globals.terms.users": "Користувачі",
|
||||
"globals.terms.year": "Рік | Роки",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "Непідтримуваний тип файлу ({type})",
|
||||
"media.upload": "Вивантажити",
|
||||
"media.uploadHelp": "Натисніть тут або посуньте сюди принаймні одну картинку",
|
||||
"media.uploadImage": "Вивантажити картинку",
|
||||
"menu.allCampaigns": "Усі кампанії",
|
||||
"menu.allLists": "Усі розсилки",
|
||||
"menu.allSubscribers": "Усі підписни_ці",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Записувати IP-адресу згоди",
|
||||
"settings.privacy.recordOptinIPHelp": "Додавати в атрибути підписни_ці IP-адресу подвійної згоди.",
|
||||
"settings.restart": "Перезапустити",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Автоматичне створення користувачів",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Автоматично створювати користувача при першому вході, якщо обліковий запис не існує.",
|
||||
"settings.security.OIDCClientID": "Ідентифікатор клієнта",
|
||||
"settings.security.OIDCClientSecret": "Секрет клієнта",
|
||||
"settings.security.OIDCDefaultListRole": "Роль списку за замовчуванням",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Роль за замовчуванням, призначена користувачам, автоматично створеним через OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Роль користувача за замовчуванням",
|
||||
"settings.security.OIDCHelp": "Увімкнути вхід OpenID Connect OAuth2 через постачальника обслуговування OAuth.",
|
||||
"settings.security.OIDCName": "Назва провайдера",
|
||||
"settings.security.OIDCRedirectURL": "URL перенаправлення для постачальника oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Схоже, що це не URL виробництва. Змініть Root URL в налаштуваннях 'Загальні'.",
|
||||
"settings.security.OIDCURL": "URL постачальника",
|
||||
"settings.security.OIDCWarning": "При ввімкненні OIDC вхід за замовчуванням з паролем вимикається. Недійсна конфігурація може заблокувати вас.",
|
||||
"settings.security.altchaComplexity": "Складність Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Вищі значення забезпечують кращий захист, але уповільнюють розв’язання (1000-1000000).",
|
||||
"settings.security.captchaKey": "SiteKey-значення hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Щоб отримати ключ і секрет, перейдіть до www.hcaptcha.com.",
|
||||
"settings.security.captchaSecret": "Секрет hCaptcha.com",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "На адресу",
|
||||
"settings.title": "Налаштування",
|
||||
"settings.updateAvailable": "Доступне оновлення {version}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Складніший запит",
|
||||
"subscribers.advancedQueryHelp": "Частковий SQL-вираз для пошуку властивостей підписни_ць",
|
||||
"subscribers.attribs": "Властивості",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "Скопіюйте токен доступу API зараз. Він не буде показаний знову.",
|
||||
"users.cantDeleteRole": "Неможливо видалити роль, яка використовується.",
|
||||
"users.firstTime": "Це свіжа установка. Виберіть ім'я користувача та пароль для облікового запису Супер адміністратора.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Недійсний логін або пароль",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Недійсний запит авторизації",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Останній вхід",
|
||||
"users.listPerms": "Дозволи списку",
|
||||
"users.listPermsWarning": "Дозволено lists:get_all або lists:manage_all, які перевизначають дозволи для окремих списків",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Вийти",
|
||||
"users.needSuper": "Користувач(і) не можуть бути оновлені. Повинен бути принаймні один активний Супер Адміністратор.",
|
||||
"users.newListRole": "Нова роль списку",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Новий користувач",
|
||||
"users.newUserRole": "Нова роль користувача",
|
||||
"users.password": "Пароль",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Повторити пароль",
|
||||
"users.perms": "Дозволи",
|
||||
"users.profile": "Профіль",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Роль | Ролі",
|
||||
"users.roleGroup": "Група",
|
||||
"users.roles": "Ролі",
|
||||
"users.status.disabled": "Вимкнено",
|
||||
"users.status.enabled": "Увімкнено",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Тип",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Супер Адміністратор",
|
||||
|
||||
120
i18n/vi.json
@@ -3,8 +3,8 @@
|
||||
"_.name": "Vietnamese (vi)",
|
||||
"admin.errorMarshallingConfig": "Lỗi sắp xếp cấu hình: {error}",
|
||||
"analytics.count": "Tổng",
|
||||
"analytics.fromDate": "Từ",
|
||||
"analytics.invalidDates": "Ngày `từ` hoặc` đến` không hợp lệ.",
|
||||
"analytics.fromDate": "Từ ngày",
|
||||
"analytics.invalidDates": "Ngày không hợp lệ.",
|
||||
"analytics.isUnique": "Số lượng là duy nhất cho mỗi người đăng ký.",
|
||||
"analytics.links": "Đường dẫn",
|
||||
"analytics.nonUnique": "Số lượng không phải là duy nhất vì theo dõi người đăng ký cá nhân bị tắt.",
|
||||
@@ -39,7 +39,7 @@
|
||||
"campaigns.customHeadersHelp": "Mảng tiêu đề tùy chỉnh để đính kèm vào thư gửi đi. ví dụ: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"campaigns.dateAndTime": "Ngày và giờ",
|
||||
"campaigns.ended": "Kết thúc",
|
||||
"campaigns.errorSendTest": "Lỗi khi gửi kiểm tra: {error}",
|
||||
"campaigns.errorSendTest": "Lỗi khi gửi email thử nghiệm: {error}",
|
||||
"campaigns.fieldInvalidBody": "Lỗi khi biên dịch nội dung chiến dịch: {error}",
|
||||
"campaigns.fieldInvalidFromEmail": "Không hợp lệ `from_email`.",
|
||||
"campaigns.fieldInvalidListIDs": "Danh sách không hợp lệ IDs.",
|
||||
@@ -50,7 +50,7 @@
|
||||
"campaigns.format": "Định dạng",
|
||||
"campaigns.formatHTML": "Định dạng HTML",
|
||||
"campaigns.fromAddress": "Từ địa chỉ",
|
||||
"campaigns.fromAddressPlaceholder": "Tên của bạn <noreply@yoursite.com>",
|
||||
"campaigns.fromAddressPlaceholder": "Tên của bạn <noreply@listmonk.host>",
|
||||
"campaigns.importVisualTemplate": "Nhập mẫu trực quan",
|
||||
"campaigns.invalid": "Chiến dịch không hợp lệ",
|
||||
"campaigns.invalidCustomHeaders": "Tiêu đề tùy chỉnh không hợp lệ: {error}",
|
||||
@@ -110,12 +110,15 @@
|
||||
"dashboard.orphanSubs": "đơn lập",
|
||||
"email.data.info": "Bản sao của tất cả dữ liệu đã ghi về bạn được đính kèm dưới dạng tệp ở định dạng JSON. Nó có thể được xem trong một trình soạn thảo văn bản.",
|
||||
"email.data.title": "Dữ liệu của bạn",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "Xác nhận đăng ký",
|
||||
"email.optin.confirmSubHelp": "Xác nhận đăng ký của bạn bằng cách nhấp vào nút bên dưới.",
|
||||
"email.optin.confirmSubInfo": "Bạn đã được thêm vào các danh sách sau:",
|
||||
"email.optin.confirmSubTitle": "Xác nhận đăng ký",
|
||||
"email.optin.confirmSubWelcome": "Xin chào",
|
||||
"email.optin.privateList": "Danh sách mật",
|
||||
"email.optin.privateList": "Danh sách riêng",
|
||||
"email.status.campaignReason": "Lý do",
|
||||
"email.status.campaignSent": "Đã gửi",
|
||||
"email.status.campaignUpdateTitle": "Cập nhật chiến dịch",
|
||||
@@ -135,7 +138,7 @@
|
||||
"forms.title": "Các hình thức",
|
||||
"globals.buttons.add": "Thêm",
|
||||
"globals.buttons.addNew": "Thêm mới",
|
||||
"globals.buttons.back": "Trở về",
|
||||
"globals.buttons.back": "Quay lại",
|
||||
"globals.buttons.cancel": "Hủy bỏ",
|
||||
"globals.buttons.clear": "Xóa",
|
||||
"globals.buttons.clearAll": "Xóa hết",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "Sao chép",
|
||||
"globals.buttons.delete": "Xóa",
|
||||
"globals.buttons.deleteAll": "Xóa hết",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "Sửa",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "Đã bật",
|
||||
"globals.buttons.insert": "Chèn",
|
||||
"globals.buttons.learnMore": "Tìm hiểu thêm",
|
||||
@@ -158,13 +163,13 @@
|
||||
"globals.buttons.saveChanges": "Lưu thay dổi",
|
||||
"globals.buttons.toggleSelect": "Chuyển đổi lựa chọn",
|
||||
"globals.buttons.view": "Xem",
|
||||
"globals.days.0": "Chủ nhật",
|
||||
"globals.days.1": "Thứ 2",
|
||||
"globals.days.2": "Thứ 3",
|
||||
"globals.days.3": "Thứ 4",
|
||||
"globals.days.4": "Thứ 5",
|
||||
"globals.days.5": "Thứ 6",
|
||||
"globals.days.6": "Thứ 7",
|
||||
"globals.days.0": "Chủ Nhật",
|
||||
"globals.days.1": "Thứ Hai",
|
||||
"globals.days.2": "Thứ Ba",
|
||||
"globals.days.3": "Thứ Tư",
|
||||
"globals.days.4": "Thứ Năm",
|
||||
"globals.days.5": "Thứ Sáu",
|
||||
"globals.days.6": "Thứ Bảy",
|
||||
"globals.days.7": "Thứ Bảy",
|
||||
"globals.fields.createdAt": "Đã tạo",
|
||||
"globals.fields.description": "Mô tả",
|
||||
@@ -174,7 +179,7 @@
|
||||
"globals.fields.type": "Kiểu",
|
||||
"globals.fields.updatedAt": "Cập nhật",
|
||||
"globals.fields.uuid": "UUID",
|
||||
"globals.messages.confirm": "Bạn chắc chưa?",
|
||||
"globals.messages.confirm": "Bạn có chắc không?",
|
||||
"globals.messages.confirmDiscard": "Loại bỏ những thay đổi?",
|
||||
"globals.messages.copied": "Đã sao chép",
|
||||
"globals.messages.created": "\"{name}\" đã tạo",
|
||||
@@ -193,11 +198,13 @@
|
||||
"globals.messages.invalidFields": "Trường không hợp lệ: {name}",
|
||||
"globals.messages.invalidID": "ID(s) không hợp lệ",
|
||||
"globals.messages.invalidUUID": " UUID(s) không hợp lệ",
|
||||
"globals.messages.missingFields": "Lỗi field(s): {name}",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "Lỗi trường: {name}",
|
||||
"globals.messages.notFound": "{name} không tìm thấy",
|
||||
"globals.messages.numSelected": "{num} đã chọn",
|
||||
"globals.messages.passwordChange": "Nhập một giá trị để thay đổi",
|
||||
"globals.messages.passwordChangeFull": "Xóa và nhập lại mật khẩu đầy đủ trong '{name}'.",
|
||||
"globals.messages.permissionDenied": "Từ chối quyền: {name}",
|
||||
"globals.messages.permissionDenied": "Quyền bị từ chối: {name}",
|
||||
"globals.messages.slowQueriesCached": "Các truy vấn chậm đang được lưu vào bộ nhớ cache. Một số con số trên trang này có thể không được cập nhật.",
|
||||
"globals.messages.updated": "\"{name}\" đã cập nhật",
|
||||
"globals.months.1": "Tháng 1",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "Mẫu | Mẫu",
|
||||
"globals.terms.templates": "Mẫu",
|
||||
"globals.terms.tx": "Giao dịch | Giao dịch",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "Người dùng | Người dùng",
|
||||
"globals.terms.users": "Người dùng",
|
||||
"globals.terms.year": "Năm | Năm",
|
||||
@@ -268,9 +276,9 @@
|
||||
"import.mode": "Chế độ",
|
||||
"import.overwrite": "Ghi đè?",
|
||||
"import.overwriteHelp": "Ghi đè tên, tiêu chí, trạng thái đăng ký của các thuê bao hiện có?",
|
||||
"import.recordsCount": "{num} / {total} Hồ sơ",
|
||||
"import.recordsCount": "{num} / {total} mục",
|
||||
"import.stopImport": "Dừng nhập",
|
||||
"import.subscribe": "Đặt mua",
|
||||
"import.subscribe": "Đăng ký",
|
||||
"import.subscribeWarning": "Ghi đè sẽ đăng ký lại các email đã hủy đăng ký. Tiếp tục?",
|
||||
"import.title": "Nhập người đăng ký",
|
||||
"import.upload": "Tải lên",
|
||||
@@ -293,7 +301,7 @@
|
||||
"maintenance.help": "Một số hoạt động có thể mất một thời gian để hoàn thành tùy thuộc vào lượng dữ liệu.",
|
||||
"maintenance.maintenance.unconfirmedOptins": "Đăng ký chưa xác nhận",
|
||||
"maintenance.olderThan": "Cũ hơn",
|
||||
"maintenance.orphanHelp": "Mồ côi = người đăng ký không có danh sách",
|
||||
"maintenance.orphanHelp": "Orphan nghĩa là người đăng ký không có danh sách",
|
||||
"maintenance.title": "Bảo trì",
|
||||
"maintenance.unconfirmedSubs": "Đăng ký chưa xác nhận cũ hơn {name} ngày.",
|
||||
"media.errorReadingFile": "Lỗi khi đọc tệp: {error}",
|
||||
@@ -304,8 +312,7 @@
|
||||
"media.title": "Phương tiện truyền thông",
|
||||
"media.unsupportedFileType": "Loại tập tin không được hỗ trợ ({type})",
|
||||
"media.upload": "Tải lên",
|
||||
"media.uploadHelp": "Nhấp hoặc kéo một hoặc nhiều hình ảnh vào đây",
|
||||
"media.uploadImage": "Tải hình ảnh lên",
|
||||
"media.uploadHelp": "Nhấp chuột hoặc kéo và thả hình ảnh vào đây",
|
||||
"menu.allCampaigns": "Tất cả chiến dịch",
|
||||
"menu.allLists": "Tất cả danh sách",
|
||||
"menu.allSubscribers": "Tất cả người đăng ký",
|
||||
@@ -352,14 +359,14 @@
|
||||
"public.privacyTitle": "Quyền riêng tư và dữ liệu",
|
||||
"public.privacyWipe": "Xóa dữ liệu của bạn",
|
||||
"public.privacyWipeHelp": "Xóa vĩnh viễn tất cả các đăng ký của bạn và dữ liệu liên quan khỏi cơ sở dữ liệu.",
|
||||
"public.sub": "Đặt mua",
|
||||
"public.sub": "Đăng ký nhận thư điện tử",
|
||||
"public.subConfirmed": "Đăng ký thành công.",
|
||||
"public.subConfirmedTitle": "Đã xác nhận",
|
||||
"public.subName": "Tên (tùy chọn)",
|
||||
"public.subNotFound": "Đăng ký không được tìm thấy.",
|
||||
"public.subOptinPending": "Một e-mail đã được gửi cho bạn để xác nhận (các) đăng ký của bạn.",
|
||||
"public.subPrivateList": "Danh sách riêng tư",
|
||||
"public.subTitle": "Đặt mua",
|
||||
"public.subTitle": "Đăng ký",
|
||||
"public.unsub": "Hủy đăng ký",
|
||||
"public.unsubFull": "Đồng thời hủy đăng ký nhận tất cả các e-mail trong tương lai.",
|
||||
"public.unsubHelp": "Bạn có muốn hủy đăng ký khỏi danh sách gửi thư này không?",
|
||||
@@ -419,12 +426,12 @@
|
||||
"settings.general.fromEmail": "Mặc định `từ` email",
|
||||
"settings.general.fromEmailHelp": "Mặc định `từ` e-mail để hiển thị trên các e-mail của chiến dịch gửi đi. Điều này có thể được thay đổi cho mỗi chiến dịch.",
|
||||
"settings.general.language": "Ngôn ngữ",
|
||||
"settings.general.logoURL": "URL logo",
|
||||
"settings.general.logoURLHelp": "(Tùy chọn) URL đầy đủ của biểu trưng tĩnh được hiển thị trên chế độ xem trực diện của người dùng, chẳng hạn như trang hủy đăng ký.",
|
||||
"settings.general.logoURL": "Đường dẫn Logo",
|
||||
"settings.general.logoURLHelp": "(Tùy chọn) URL đầy đủ của logo. Sẽ được hiển thị trên trang đăng ký / hủy đăng ký.",
|
||||
"settings.general.name": "Tổng quan",
|
||||
"settings.general.rootURL": "Gốc URL",
|
||||
"settings.general.rootURLHelp": "URL công khai của cài đặt (không có dấu gạch chéo).",
|
||||
"settings.general.sendOptinConfirm": "Gửi xác nhận chọn tham gia",
|
||||
"settings.general.rootURL": "Đường dẫn",
|
||||
"settings.general.rootURLHelp": "Đường dẫn công khai của cài đặt (không có dấu gạch chéo).",
|
||||
"settings.general.sendOptinConfirm": "Gửi xác nhận đăng ký tham gia bản tin",
|
||||
"settings.general.sendOptinConfirmHelp": "Gửi e-mail xác nhận chọn tham gia khi người đăng ký đăng ký qua biểu mẫu công khai hoặc khi họ được thêm bởi quản trị viên.",
|
||||
"settings.general.siteName": "Tên trang web",
|
||||
"settings.invalidMessengerName": "Tên người đưa tin không hợp lệ.",
|
||||
@@ -433,8 +440,8 @@
|
||||
"settings.mailserver.hostHelp": "Địa chỉ máy chủ của máy chủ SMTP.",
|
||||
"settings.mailserver.idleTimeout": "Thời gian chờ nhàn rỗi",
|
||||
"settings.mailserver.idleTimeoutHelp": "Thời gian chờ hoạt động mới trên một kết nối trước khi đóng và xóa nó khỏi nhóm (s cho giây, m cho phút).",
|
||||
"settings.mailserver.maxConns": "Tối đa kết nối",
|
||||
"settings.mailserver.maxConnsHelp": "Kết nối đồng thời tối đa đến máy chủ.",
|
||||
"settings.mailserver.maxConns": "Số lượng kết nối tối đa",
|
||||
"settings.mailserver.maxConnsHelp": "Số lượng kết nối đồng thời đến máy chủ.",
|
||||
"settings.mailserver.nameHelp": "Tên duy nhất tùy chọn cho máy chủ SMTP. Phải có tiền tố email-. Cài đặt này cho phép chọn máy chủ cụ thể cho một chiến dịch. ví dụ: email-primary-server. Ký tự chữ và số / dấu gạch ngang.",
|
||||
"settings.mailserver.password": "Mật khẩu",
|
||||
"settings.mailserver.passwordHelp": "Nhập để thay đổi",
|
||||
@@ -470,7 +477,7 @@
|
||||
"settings.media.upload.path": "Đường dẫn tải lên",
|
||||
"settings.media.upload.pathHelp": "Đường dẫn đến thư mục nơi phương tiện sẽ được tải lên.",
|
||||
"settings.media.upload.uri": "Tải lên URI",
|
||||
"settings.media.upload.uriHelp": "Tải lên URI hiển thị với thế giới bên ngoài. Phương tiện được tải lên upload_path sẽ có thể truy cập công khai trong {root_url}, chẳng hạn như https://listmonk.yoursite.com/uploads.",
|
||||
"settings.media.upload.uriHelp": "Tải lên URI hiển thị với thế giới bên ngoài. Phương tiện được tải lên upload_path sẽ có thể truy cập công khai trong {root_url}, ví dụ như https://listmonk.host/uploads.",
|
||||
"settings.messengers.maxConns": "Tối đa kết nối",
|
||||
"settings.messengers.maxConnsHelp": "Kết nối đồng thời tối đa đến máy chủ.",
|
||||
"settings.messengers.messageSaved": "Đã lưu cài đặt. Đang tải lại ứng dụng ...",
|
||||
@@ -486,10 +493,10 @@
|
||||
"settings.messengers.urlHelp": "URL gốc của máy chủ Đăng lại.",
|
||||
"settings.messengers.username": "Tài khoản",
|
||||
"settings.needsRestart": "Đã thay đổi cài đặt. Tạm dừng tất cả các chiến dịch đang chạy và khởi động lại ứng dụng",
|
||||
"settings.performance.batchSize": "Kích thước lô",
|
||||
"settings.performance.batchSize": "Kích thước cho một lô gửi",
|
||||
"settings.performance.batchSizeHelp": "Số lượng người đăng ký để lấy từ cơ sở dữ liệu trong một lần lặp lại. Mỗi lần lặp lại kéo người đăng ký từ cơ sở dữ liệu, gửi tin nhắn cho họ, sau đó chuyển sang lần lặp tiếp theo để kéo đợt tiếp theo. Điều này lý tưởng là phải cao hơn thông lượng tối đa có thể đạt được (đồng thời * message_rate).",
|
||||
"settings.performance.cacheSlowQueries": "Lưu vào bộ nhớ cache các truy vấn cơ sở dữ liệu chậm",
|
||||
"settings.performance.cacheSlowQueriesHelp": "Chỉ bật tính năng này trên các cơ sở dữ liệu lớn đã bị chậm hiện tại. Lưu ý rằng tính năng này sẽ tạo bộ nhớ đệm cho số lượng người đăng ký danh sách, thống kê bảng điều khiển, v.v.",
|
||||
"settings.performance.cacheSlowQueriesHelp": "Chỉ bật tính năng này trên các cơ sở dữ liệu lớn và hiệu năng có dấu hiệu giảm sút. Lưu ý rằng tính năng này sẽ tạo bộ nhớ đệm cho số lượng người đăng ký danh sách, thống kê bảng điều khiển, v.v.",
|
||||
"settings.performance.concurrency": "Đồng thời",
|
||||
"settings.performance.concurrencyHelp": "Công nhân đồng thời tối đa (luồng) sẽ cố gắng gửi tin nhắn đồng thời.",
|
||||
"settings.performance.maxErrThreshold": "Ngưỡng lỗi tối đa",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "Ghi lại IP đăng ký",
|
||||
"settings.privacy.recordOptinIPHelp": "Ghi lại địa chỉ IP của đăng ký kép vào thuộc tính của người đăng ký.",
|
||||
"settings.restart": "Khởi động lại",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "Tự động tạo người dùng",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "Tự động tạo người dùng khi đăng nhập lần đầu nếu tài khoản chưa tồn tại.",
|
||||
"settings.security.OIDCClientID": "Mã client ID",
|
||||
"settings.security.OIDCClientSecret": "Mã client secret",
|
||||
"settings.security.OIDCDefaultListRole": "Vai trò danh sách mặc định",
|
||||
"settings.security.OIDCDefaultRoleHelp": "Vai trò mặc định được gán cho người dùng được tạo tự động từ OIDC.",
|
||||
"settings.security.OIDCDefaultUserRole": "Vai trò người dùng mặc định",
|
||||
"settings.security.OIDCHelp": "Bật đăng nhập OpenID Connect OAuth2 thông qua một nhà cung cấp OAuth.",
|
||||
"settings.security.OIDCName": "Tên nhà cung cấp",
|
||||
"settings.security.OIDCRedirectURL": "URL chuyển hướng cho nhà cung cấp oAuth",
|
||||
"settings.security.OIDCRedirectWarning": "Đây không phải là URL sản xuất. Thay đổi Root URL trong cài đặt 'Chung'.",
|
||||
"settings.security.OIDCURL": "URL nhà cung cấp",
|
||||
"settings.security.OIDCWarning": "Khi OIDC được bật, đăng nhập mặc định bằng mật khẩu sẽ bị vô hiệu. Cấu hình không hợp lệ có thể khóa bạn ra ngoài.",
|
||||
"settings.security.altchaComplexity": "Độ phức tạp Altcha",
|
||||
"settings.security.altchaComplexityHelp": "Giá trị cao hơn cung cấp bảo mật tốt hơn nhưng thời gian giải chậm hơn (1000-1000000).",
|
||||
"settings.security.captchaKey": "Khóa trang hCaptcha.com",
|
||||
"settings.security.captchaKeyHelp": "Truy cập www.hcaptcha.com để lấy khóa và bí mật.",
|
||||
"settings.security.captchaSecret": "Bí mật trang hCaptcha.com",
|
||||
@@ -541,10 +557,10 @@
|
||||
"settings.smtp.customHeaders": "Tiêu đề tùy chỉnh",
|
||||
"settings.smtp.customHeadersHelp": "Mảng tiêu đề e-mail tùy chọn để bao gồm trong tất cả các thư được gửi từ máy chủ này. ví dụ: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
|
||||
"settings.smtp.enabled": "Đã bật",
|
||||
"settings.smtp.heloHost": "Xin chào host",
|
||||
"settings.smtp.heloHostHelp": "Không bắt buộc. Một số máy chủ SMTP yêu cầu FQDN trong tên máy chủ. Theo mặc định, HELLO đi cùng với `localhost`. Đặt điều này nếu một tên máy chủ tùy chỉnh được sử dụng.",
|
||||
"settings.smtp.heloHost": "HELO hostname của email server",
|
||||
"settings.smtp.heloHostHelp": "Không bắt buộc. Một số máy chủ SMTP yêu cầu FQDN trong tên máy chủ. Theo mặc định, HELO đi cùng với `localhost`. Tùy chỉnh theo máy chủ của bạn.",
|
||||
"settings.smtp.name": "SMTP",
|
||||
"settings.smtp.retries": "Thử lại",
|
||||
"settings.smtp.retries": "Số lần thử lại",
|
||||
"settings.smtp.retriesHelp": "Số lần thử lại khi có thông báo không thành công.",
|
||||
"settings.smtp.sendTest": "Gửi email",
|
||||
"settings.smtp.setCustomHeaders": "Đặt tiêu đề tùy chỉnh",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "Email đến",
|
||||
"settings.title": "Cài đặt",
|
||||
"settings.updateAvailable": "Đã có bản cập nhật mới {version}.",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "Trình độ cao",
|
||||
"subscribers.advancedQueryHelp": "Biểu thức SQL một phần để truy vấn thuộc tính người đăng ký",
|
||||
"subscribers.attribs": "Thuộc tính",
|
||||
@@ -561,7 +578,7 @@
|
||||
"subscribers.confirmBlocklist": "Danh sách chặn {num} người đăng ký?",
|
||||
"subscribers.confirmDelete": "Xóa {num} người đăng ký?",
|
||||
"subscribers.confirmExport": "Xuất {num} người đăng ký?",
|
||||
"subscribers.domainBlocklisted": "Miền email được đưa vào danh sách đen.",
|
||||
"subscribers.domainBlocklisted": "Tên miền của email đã bị đưa vào danh sách đen.",
|
||||
"subscribers.downloadData": "Tải xuống dữ liệu",
|
||||
"subscribers.email": "Email",
|
||||
"subscribers.emailExists": "E-mail đã tồn tại",
|
||||
@@ -569,7 +586,7 @@
|
||||
"subscribers.errorNoIDs": "Không có ID nào được cung cấp.",
|
||||
"subscribers.errorNoListsGiven": "Không có danh sách nào được đưa ra.",
|
||||
"subscribers.errorPreparingQuery": "Lỗi khi chuẩn bị truy vấn người đăng ký: {error}",
|
||||
"subscribers.errorSendingOptin": "Lỗi khi gửi e-mail chọn tham gia.",
|
||||
"subscribers.errorSendingOptin": "Lỗi khi gửi e-mail đăng ký.",
|
||||
"subscribers.export": "Xuất",
|
||||
"subscribers.invalidAction": "Hành động không hợp lệ.",
|
||||
"subscribers.invalidEmail": "Email không hợp lệ.",
|
||||
@@ -587,12 +604,12 @@
|
||||
"subscribers.preconfirm": "Xác nhận trước đăng ký",
|
||||
"subscribers.preconfirmHelp": "Không gửi e-mail chọn tham gia và đánh dấu tất cả các đăng ký trong danh sách là 'đã đăng ký'.",
|
||||
"subscribers.query": "Truy vấn",
|
||||
"subscribers.queryPlaceholder": "E-mail or tên",
|
||||
"subscribers.reset": "Cài lại",
|
||||
"subscribers.queryPlaceholder": "E-mail hoặc Tên",
|
||||
"subscribers.reset": "Đặt lại",
|
||||
"subscribers.selectAll": "Chọn tất cả {num}",
|
||||
"subscribers.sendOptinConfirm": "Gửi xác nhận chọn tham gia",
|
||||
"subscribers.sentOptinConfirm": "Đã gửi xác nhận chọn tham gia",
|
||||
"subscribers.status.blocklisted": "Bị chặn",
|
||||
"subscribers.status.blocklisted": "Đã bị chặn",
|
||||
"subscribers.status.confirmed": "Đã xác nhận",
|
||||
"subscribers.status.enabled": "Đã bật",
|
||||
"subscribers.status.subscribed": "Đã đăng ký",
|
||||
@@ -608,7 +625,7 @@
|
||||
"templates.fieldInvalidName": "Độ dài không hợp lệ cho tên.",
|
||||
"templates.makeDefault": "Đặt mặc định",
|
||||
"templates.newTemplate": "Mẫu mới",
|
||||
"templates.placeholderHelp": "Trình giữ chỗ {placeholder} sẽ xuất hiện chính xác một lần trong mẫu.",
|
||||
"templates.placeholderHelp": "Dữ liệu thay thế {placeholder} sẽ xuất hiện chính xác một lần trong mẫu.",
|
||||
"templates.preview": "Xem trước",
|
||||
"templates.rawHTML": "HTML thô",
|
||||
"templates.subject": "Chủ đề",
|
||||
@@ -617,9 +634,12 @@
|
||||
"templates.typeTransactional": "Giao dịch",
|
||||
"users.apiOneTimeToken": "Sao chép mã truy cập API ngay bây giờ. Nó sẽ không được hiển thị lại.",
|
||||
"users.cantDeleteRole": "Không thể xóa vai trò đã được sử dụng.",
|
||||
"users.firstTime": "Đây là cài đặt mới. Chọn tên người dùng và mật khẩu cho tài khoản Super Admin.",
|
||||
"users.firstTime": "Đây là lần cài đặt đầu tiên. Chọn tên người dùng và mật khẩu cho tài khoản Super Admin.",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "Đăng nhập hoặc mật khẩu không hợp lệ",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "Yêu cầu xác thực không hợp lệ",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "Lần đăng nhập gần nhất",
|
||||
"users.listPerms": "Quyền cho danh sách",
|
||||
"users.listPermsWarning": "Đã bật lists:get_all hoặc lists:manage_all, ghi đè quyền theo từng danh sách",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "Đăng xuất",
|
||||
"users.needSuper": "Người dùng(s) không thể được cập nhật. Phải có ít nhất một người dùng Super Admin hoạt động.",
|
||||
"users.newListRole": "Vai trò danh sách mới",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "Người dùng mới",
|
||||
"users.newUserRole": "Vai trò người dùng mới",
|
||||
"users.password": "Mật khẩu",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "Lặp lại mật khẩu",
|
||||
"users.perms": "Quyền",
|
||||
"users.profile": "Hồ sơ",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "Vai trò | Vai trò",
|
||||
"users.roleGroup": "Nhóm",
|
||||
"users.roles": "Vai trò",
|
||||
"users.status.disabled": "Vô hiệu hóa",
|
||||
"users.status.enabled": "Đã kích hoạt",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "Loại",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "Super Admin",
|
||||
@@ -650,5 +682,5 @@
|
||||
"users.userRole": "Vai trò người dùng | Vai trò người dùng",
|
||||
"users.userRoles": "Vai trò người dùng",
|
||||
"users.username": "Tên người dùng",
|
||||
"users.usernameHelp": "Sử dụng với đăng nhập bằng mật khẩu"
|
||||
"users.usernameHelp": "Sử dụng khi đăng nhập bằng mật khẩu"
|
||||
}
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "孤儿",
|
||||
"email.data.info": "记录在您身上的所有数据的副本作为 JSON 格式的文件附加。它可以在文本编辑器中查看。",
|
||||
"email.data.title": "您的数据",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "确认订阅",
|
||||
"email.optin.confirmSubHelp": "单击下面的按钮确认您的订阅",
|
||||
"email.optin.confirmSubInfo": "您已被添加到以下列表中",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "复制",
|
||||
"globals.buttons.delete": "删除",
|
||||
"globals.buttons.deleteAll": "删除所有",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "编辑",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "启用",
|
||||
"globals.buttons.insert": "添加",
|
||||
"globals.buttons.learnMore": "更多",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "无效字段:{name}",
|
||||
"globals.messages.invalidID": "ID 无效",
|
||||
"globals.messages.invalidUUID": "无效的 UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "缺少字段:{name}",
|
||||
"globals.messages.notFound": "{name} 未找到",
|
||||
"globals.messages.numSelected": "已选择 {num}",
|
||||
"globals.messages.passwordChange": "输入要更改的值",
|
||||
"globals.messages.passwordChangeFull": "在“{name}”中清除并重新输入完整密码。",
|
||||
"globals.messages.permissionDenied": "权限被拒绝:{name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "模板 | 多个模板",
|
||||
"globals.terms.templates": "模板",
|
||||
"globals.terms.tx": "交易 | 交易",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "用户",
|
||||
"globals.terms.users": "用户",
|
||||
"globals.terms.year": "年 | 多年",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "不支持的文件类型 ({type})",
|
||||
"media.upload": "上传",
|
||||
"media.uploadHelp": "在此处单击或拖动一张或多张图片",
|
||||
"media.uploadImage": "上传图片",
|
||||
"menu.allCampaigns": "所有广告系列",
|
||||
"menu.allLists": "所有列表",
|
||||
"menu.allSubscribers": "所有订阅者",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "记录开通IP地址",
|
||||
"settings.privacy.recordOptinIPHelp": "在订阅者属性中记录双选订阅的IP地址。",
|
||||
"settings.restart": "重新开始",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "自动创建用户",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "如果账户不存在,首次登录时自动创建用户。",
|
||||
"settings.security.OIDCClientID": "客户端ID",
|
||||
"settings.security.OIDCClientSecret": "客户端密钥",
|
||||
"settings.security.OIDCDefaultListRole": "默认列表角色",
|
||||
"settings.security.OIDCDefaultRoleHelp": "自动通过 OIDC 创建的用户分配的默认角色。",
|
||||
"settings.security.OIDCDefaultUserRole": "默认用户角色",
|
||||
"settings.security.OIDCHelp": "通过OAuth提供程序启用OpenID Connect OAuth2登录。",
|
||||
"settings.security.OIDCName": "Provider name",
|
||||
"settings.security.OIDCRedirectURL": "oAuth提供程序的重定向URL",
|
||||
"settings.security.OIDCRedirectWarning": "这似乎不是生产URL。在“常规”设置中更改根URL。",
|
||||
"settings.security.OIDCURL": "提供程序URL",
|
||||
"settings.security.OIDCWarning": "启用OIDC时,默认密码登录将被禁用。无效的配置可能会使您被锁定。",
|
||||
"settings.security.altchaComplexity": "Altcha 复杂度",
|
||||
"settings.security.altchaComplexityHelp": "数值越高安全性越好,但解题速度越慢(1000-1000000)。",
|
||||
"settings.security.captchaKey": "hCaptcha.com站点密钥",
|
||||
"settings.security.captchaKeyHelp": "访问www.hcaptcha.com获取密钥和秘密。",
|
||||
"settings.security.captchaSecret": "hCaptcha.com秘密",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "发到邮箱",
|
||||
"settings.title": "设置",
|
||||
"settings.updateAvailable": "有新的更新 {version} 可用。",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "高级",
|
||||
"subscribers.advancedQueryHelp": "查询订阅者属性的部分SQL表达式",
|
||||
"subscribers.attribs": "属性",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "立即复制API访问令牌。不会再显示。",
|
||||
"users.cantDeleteRole": "无法删除正在使用的角色。",
|
||||
"users.firstTime": "这是一次全新安装。为超级管理员帐户选择用户名和密码。",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "无效的登录或密码",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "无效的身份验证请求",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "上次登录",
|
||||
"users.listPerms": "列出权限",
|
||||
"users.listPermsWarning": "已启用lists:get_all或lists:manage_all,这将覆盖每个列表的权限",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "登出",
|
||||
"users.needSuper": "无法更新用户。必须至少有一个活动的超级管理员用户。",
|
||||
"users.newListRole": "新列表角色",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "新用户",
|
||||
"users.newUserRole": "新用户角色",
|
||||
"users.password": "密码",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "重复密码",
|
||||
"users.perms": "权限",
|
||||
"users.profile": "个人资料",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "角色",
|
||||
"users.roleGroup": "组",
|
||||
"users.roles": "角色",
|
||||
"users.status.disabled": "已禁用",
|
||||
"users.status.enabled": "已启用",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "类型",
|
||||
"users.type.api": "API",
|
||||
"users.type.super": "超级管理员",
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
"dashboard.orphanSubs": "孤兒",
|
||||
"email.data.info": "記錄在您身上的所有資料副本作為 JSON 格式的文件附加。它可以在文本編輯器中檢視。",
|
||||
"email.data.title": "您的數據",
|
||||
"email.forgotPassword.button": "Reset password",
|
||||
"email.forgotPassword.info": "If you didn't request this, you can safely ignore this email. This link will expire in 30 minutes.",
|
||||
"email.forgotPassword.subject": "Reset your password",
|
||||
"email.optin.confirmSub": "確認訂閱",
|
||||
"email.optin.confirmSubHelp": "點擊下面的按鈕來確認您的訂閱",
|
||||
"email.optin.confirmSubInfo": "您已被新增到以下清單中",
|
||||
@@ -145,7 +148,9 @@
|
||||
"globals.buttons.copy": "複製",
|
||||
"globals.buttons.delete": "刪除",
|
||||
"globals.buttons.deleteAll": "刪除所有",
|
||||
"globals.buttons.disable": "Disable",
|
||||
"globals.buttons.edit": "編輯",
|
||||
"globals.buttons.enable": "Enable",
|
||||
"globals.buttons.enabled": "啟用",
|
||||
"globals.buttons.insert": "插入",
|
||||
"globals.buttons.learnMore": "更多",
|
||||
@@ -193,8 +198,10 @@
|
||||
"globals.messages.invalidFields": "無效的欄位: {name}",
|
||||
"globals.messages.invalidID": "ID 無效",
|
||||
"globals.messages.invalidUUID": "無效的 UUID",
|
||||
"globals.messages.invalidValue": "Invalid value",
|
||||
"globals.messages.missingFields": "缺少欄位:{name}",
|
||||
"globals.messages.notFound": "{name} 未找到",
|
||||
"globals.messages.numSelected": "已選擇 {num}",
|
||||
"globals.messages.passwordChange": "輸入要變更的密碼",
|
||||
"globals.messages.passwordChangeFull": "在 '{name}' 中清除並重新輸入完整密碼。",
|
||||
"globals.messages.permissionDenied": "權限拒絕: {name}",
|
||||
@@ -242,6 +249,7 @@
|
||||
"globals.terms.template": "版型| 多個版型",
|
||||
"globals.terms.templates": "版型",
|
||||
"globals.terms.tx": "交易 | 交易",
|
||||
"globals.terms.url": "URL",
|
||||
"globals.terms.user": "使用者 | 使用者",
|
||||
"globals.terms.users": "使用者",
|
||||
"globals.terms.year": "年| 多年",
|
||||
@@ -305,7 +313,6 @@
|
||||
"media.unsupportedFileType": "不支援的檔案類型({type})",
|
||||
"media.upload": "上傳",
|
||||
"media.uploadHelp": "在此處點擊或拖曳一張或多張圖片",
|
||||
"media.uploadImage": "上傳圖片",
|
||||
"menu.allCampaigns": "所有廣告",
|
||||
"menu.allLists": "所有清單",
|
||||
"menu.allSubscribers": "所有訂閱者",
|
||||
@@ -523,14 +530,23 @@
|
||||
"settings.privacy.recordOptinIP": "記錄訂閱同意的 IP 位址",
|
||||
"settings.privacy.recordOptinIPHelp": "在訂閱者屬性中記錄 double opt-ins 的 IP 位址。",
|
||||
"settings.restart": "重新開始",
|
||||
"settings.security.CORSDomains": "Allowed origins",
|
||||
"settings.security.CORSDomainsHelp": "Permit accessing API endpoints via browser Javascript from external domains. Enter one domain per line (e.g: https://example.com). Leave empty to disable CORS or add * to allow all (not recommended).",
|
||||
"settings.security.OIDCAutoCreateUsers": "自動建立使用者",
|
||||
"settings.security.OIDCAutoCreateUsersHelp": "若帳號不存在,首次登入時自動建立使用者。",
|
||||
"settings.security.OIDCClientID": "用戶端ID",
|
||||
"settings.security.OIDCClientSecret": "用戶端密鑰",
|
||||
"settings.security.OIDCDefaultListRole": "預設名單角色",
|
||||
"settings.security.OIDCDefaultRoleHelp": "從 OIDC 自動建立的使用者所指派的預設角色。",
|
||||
"settings.security.OIDCDefaultUserRole": "預設使用者角色",
|
||||
"settings.security.OIDCHelp": "啟用 OpenID Connect OAuth2 登入,透過 OAuth 提供者。",
|
||||
"settings.security.OIDCName": "提供者名稱",
|
||||
"settings.security.OIDCRedirectURL": "oAuth 提供者的重新導向網址",
|
||||
"settings.security.OIDCRedirectWarning": "這似乎不是生產網址。請更改「一般」設定中的根網址。",
|
||||
"settings.security.OIDCURL": "提供者網址",
|
||||
"settings.security.OIDCWarning": "啟用 OIDC 後,預設密碼登入將被停用。無效的設定可能導致你無法登入。",
|
||||
"settings.security.altchaComplexity": "Altcha 複雜度",
|
||||
"settings.security.altchaComplexityHelp": "數值越高安全性越佳,但解題速度越慢(1000-1000000)。",
|
||||
"settings.security.captchaKey": "hCaptcha.com 網站金鑰",
|
||||
"settings.security.captchaKeyHelp": "開啟 www.hcaptcha.com 獲取金鑰和密鑰。",
|
||||
"settings.security.captchaSecret": "hCaptcha.com 密鑰",
|
||||
@@ -553,6 +569,7 @@
|
||||
"settings.smtp.toEmail": "電子郵件至",
|
||||
"settings.title": "設定",
|
||||
"settings.updateAvailable": "有新的更新 {version} 可用。",
|
||||
"subscribers.activity": "Activity",
|
||||
"subscribers.advancedQuery": "高級",
|
||||
"subscribers.advancedQueryHelp": "查看訂閱者屬性的部分 SQL 表達式",
|
||||
"subscribers.attribs": "屬性",
|
||||
@@ -618,8 +635,11 @@
|
||||
"users.apiOneTimeToken": "立即複製 API 存取權杖。將不再顯示。",
|
||||
"users.cantDeleteRole": "無法刪除正在使用的角色。",
|
||||
"users.firstTime": "這是全新的安裝。為超級管理員帳戶選擇使用者名稱和密碼。",
|
||||
"users.forgotPassword": "Forgot password?",
|
||||
"users.invalidLogin": "登入或密碼無效",
|
||||
"users.invalidPassword": "Invalid password",
|
||||
"users.invalidRequest": "無效的身份驗證請求",
|
||||
"users.invalidResetLink": "Invalid or expired reset link",
|
||||
"users.lastLogin": "上次登入",
|
||||
"users.listPerms": "清單權限",
|
||||
"users.listPermsWarning": "啟用 lists:get_all 或 lists:manage_all 將覆蓋每個清單的權限設定",
|
||||
@@ -630,6 +650,7 @@
|
||||
"users.logout": "登出",
|
||||
"users.needSuper": "使用者無法更新。至少需要一個有效的超級管理員用戶。",
|
||||
"users.newListRole": "新清單角色",
|
||||
"users.newPassword": "New password",
|
||||
"users.newUser": "新使用者",
|
||||
"users.newUserRole": "新使用者角色",
|
||||
"users.password": "密碼",
|
||||
@@ -638,11 +659,22 @@
|
||||
"users.passwordRepeat": "重複密碼",
|
||||
"users.perms": "權限",
|
||||
"users.profile": "個人資料",
|
||||
"users.resetLinkSent": "If the account is active, you will receive a password reset email.",
|
||||
"users.resetPassword": "Reset password",
|
||||
"users.role": "角色 | 角色",
|
||||
"users.roleGroup": "使用者群組",
|
||||
"users.roles": "用戶角色",
|
||||
"users.status.disabled": "已停用",
|
||||
"users.status.enabled": "已啟用",
|
||||
"users.totpCode": "TOTP code",
|
||||
"users.totpCodeHelp": "Enter the 6-digit code from your authenticator app",
|
||||
"users.totpScanQR": "Scan the QR code with your authenticator app such as Ente or Google Authenticator and enter the TOTP code below.",
|
||||
"users.totpSecret": "Secret key",
|
||||
"users.twoFA": "Two-factor authentication",
|
||||
"users.twoFAAlreadyEnabled": "Two-factor authentication is already enabled.",
|
||||
"users.twoFAEnabled": "Two-factor authentication is on",
|
||||
"users.twoFAEnabledDesc": "Your account is protected with {type} 2FA",
|
||||
"users.twoFANotEnabled": "Enable 2FA for additional security when logging into your account.",
|
||||
"users.type": "用戶類型",
|
||||
"users.type.api": "API 用戶",
|
||||
"users.type.super": "超級管理員",
|
||||
|
||||
@@ -21,18 +21,23 @@ import (
|
||||
)
|
||||
|
||||
type OIDCclaim struct {
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
Sub string `json:"sub"`
|
||||
Picture string `json:"picture"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
Sub string `json:"sub"`
|
||||
Picture string `json:"picture"`
|
||||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
}
|
||||
|
||||
type OIDCConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
ProviderURL string `json:"provider_url"`
|
||||
RedirectURL string `json:"redirect_url"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
Enabled bool `json:"enabled"`
|
||||
ProviderURL string `json:"provider_url"`
|
||||
RedirectURL string `json:"redirect_url"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
AutoCreateUsers bool `json:"auto_create_users"`
|
||||
DefaultUserRoleID int `json:"default_user_role_id"`
|
||||
DefaultListRoleID int `json:"default_list_role_id"`
|
||||
}
|
||||
|
||||
type BasicAuthConfig struct {
|
||||
@@ -79,28 +84,6 @@ func New(cfg Config, db *sql.DB, cb *Callbacks, lo *log.Logger) (*Auth, error) {
|
||||
apiUsers: map[string]User{},
|
||||
}
|
||||
|
||||
// Initialize OIDC.
|
||||
if cfg.OIDC.Enabled {
|
||||
provider, err := oidc.NewProvider(context.Background(), cfg.OIDC.ProviderURL)
|
||||
if err != nil {
|
||||
cfg.OIDC.Enabled = false
|
||||
lo.Printf("error initializing OIDC OAuth provider: %v", err)
|
||||
} else {
|
||||
a.verifier = provider.Verifier(&oidc.Config{
|
||||
ClientID: cfg.OIDC.ClientID,
|
||||
})
|
||||
|
||||
a.oauthCfg = oauth2.Config{
|
||||
ClientID: cfg.OIDC.ClientID,
|
||||
ClientSecret: cfg.OIDC.ClientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
RedirectURL: cfg.OIDC.RedirectURL,
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
a.provider = provider
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize session manager.
|
||||
a.sess = simplesessions.New(simplesessions.Options{
|
||||
EnableAutoCreate: false,
|
||||
@@ -162,15 +145,91 @@ func (o *Auth) GetAPIToken(user string, token string) (User, bool) {
|
||||
return t, true
|
||||
}
|
||||
|
||||
// initOIDC initializes the OIDC provider, verifier, and OAuth config.
|
||||
func (o *Auth) initOIDC() error {
|
||||
if !o.cfg.OIDC.Enabled {
|
||||
return fmt.Errorf("OIDC is not enabled")
|
||||
}
|
||||
|
||||
provider, err := oidc.NewProvider(context.Background(), o.cfg.OIDC.ProviderURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initializing OIDC OAuth provider: %v", err)
|
||||
}
|
||||
|
||||
o.verifier = provider.Verifier(&oidc.Config{
|
||||
ClientID: o.cfg.OIDC.ClientID,
|
||||
})
|
||||
|
||||
o.oauthCfg = oauth2.Config{
|
||||
ClientID: o.cfg.OIDC.ClientID,
|
||||
ClientSecret: o.cfg.OIDC.ClientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
RedirectURL: o.cfg.OIDC.RedirectURL,
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
o.provider = provider
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getProvider returns the OIDC provider, initializing it if necessary.
|
||||
func (o *Auth) getProvider() (*oidc.Provider, error) {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
|
||||
if o.provider == nil {
|
||||
if err := o.initOIDC(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return o.provider, nil
|
||||
}
|
||||
|
||||
// getVerifier returns the OIDC verifier, initializing it if necessary.
|
||||
func (o *Auth) getVerifier() (*oidc.IDTokenVerifier, error) {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
|
||||
if o.verifier == nil {
|
||||
if err := o.initOIDC(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return o.verifier, nil
|
||||
}
|
||||
|
||||
// getOAuthConfig returns the OAuth config, initializing it if necessary.
|
||||
func (o *Auth) getOAuthConfig() (*oauth2.Config, error) {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
|
||||
if o.oauthCfg.ClientID == "" {
|
||||
if err := o.initOIDC(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &o.oauthCfg, nil
|
||||
}
|
||||
|
||||
// GetOIDCAuthURL returns the OIDC provider's auth URL to redirect to.
|
||||
func (o *Auth) GetOIDCAuthURL(state, nonce string) string {
|
||||
return o.oauthCfg.AuthCodeURL(state, oidc.Nonce(nonce))
|
||||
cfg, err := o.getOAuthConfig()
|
||||
if err != nil {
|
||||
o.log.Printf("error getting OAuth config: %v", err)
|
||||
return ""
|
||||
}
|
||||
return cfg.AuthCodeURL(state, oidc.Nonce(nonce))
|
||||
}
|
||||
|
||||
// ExchangeOIDCToken takes an OIDC authorization code (recieved via redirect from the OIDC provider),
|
||||
// validates it, and returns an OIDC token for subsequent auth.
|
||||
func (o *Auth) ExchangeOIDCToken(code, nonce string) (string, OIDCclaim, error) {
|
||||
tk, err := o.oauthCfg.Exchange(context.TODO(), code)
|
||||
cfg, err := o.getOAuthConfig()
|
||||
if err != nil {
|
||||
return "", OIDCclaim{}, echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("error getting OAuth config: %v", err))
|
||||
}
|
||||
|
||||
tk, err := cfg.Exchange(context.TODO(), code)
|
||||
if err != nil {
|
||||
return "", OIDCclaim{}, echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("error exchanging token: %v", err))
|
||||
}
|
||||
@@ -180,7 +239,12 @@ func (o *Auth) ExchangeOIDCToken(code, nonce string) (string, OIDCclaim, error)
|
||||
return "", OIDCclaim{}, echo.NewHTTPError(http.StatusUnauthorized, "`id_token` missing.")
|
||||
}
|
||||
|
||||
idTk, err := o.verifier.Verify(context.TODO(), rawIDTk)
|
||||
verifier, err := o.getVerifier()
|
||||
if err != nil {
|
||||
return "", OIDCclaim{}, echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("error getting verifier: %v", err))
|
||||
}
|
||||
|
||||
idTk, err := verifier.Verify(context.TODO(), rawIDTk)
|
||||
if err != nil {
|
||||
return "", OIDCclaim{}, echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("error verifying ID token: %v", err))
|
||||
}
|
||||
@@ -196,7 +260,12 @@ func (o *Auth) ExchangeOIDCToken(code, nonce string) (string, OIDCclaim, error)
|
||||
|
||||
// If claims doesn't have the e-mail, attempt to fetch it from the userinfo endpoint.
|
||||
if claims.Email == "" {
|
||||
userInfo, err := o.provider.UserInfo(context.TODO(), oauth2.StaticTokenSource(tk))
|
||||
provider, err := o.getProvider()
|
||||
if err != nil {
|
||||
return "", OIDCclaim{}, fmt.Errorf("error getting provider: %v", err)
|
||||
}
|
||||
|
||||
userInfo, err := provider.UserInfo(context.TODO(), oauth2.StaticTokenSource(tk))
|
||||
if err != nil {
|
||||
return "", OIDCclaim{}, errors.New("error fetching user info from OIDC")
|
||||
}
|
||||
@@ -331,7 +400,7 @@ func (o *Auth) validateSession(c echo.Context) (*simplesessions.Session, User, e
|
||||
userID, err := o.sessStore.Int(vars["user_id"], nil)
|
||||
if err != nil || userID < 1 {
|
||||
o.log.Printf("error fetching session user ID: %v", err)
|
||||
return nil, User{}, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
return nil, User{}, echo.NewHTTPError(http.StatusInternalServerError, "invalid session.")
|
||||
}
|
||||
|
||||
// Fetch user details from the database.
|
||||
|
||||
@@ -95,6 +95,8 @@ type User struct {
|
||||
Type string `db:"type" json:"type"`
|
||||
Status string `db:"status" json:"status"`
|
||||
Avatar null.String `db:"avatar" json:"avatar"`
|
||||
TwofaType string `db:"twofa_type" json:"twofa_type"`
|
||||
TwofaKey null.String `db:"twofa_key" json:"-"`
|
||||
LoggedInAt null.Time `db:"loggedin_at" json:"loggedin_at"`
|
||||
UserRoleID int `db:"user_role_id" json:"user_role_id,omitempty"`
|
||||
UserRoleName string `db:"user_role_name" json:"-"`
|
||||
|
||||
@@ -125,6 +125,7 @@ func (m *Manager) Run() {
|
||||
// runMailboxScanner runs a blocking loop that scans the mailbox at given intervals.
|
||||
func (m *Manager) runMailboxScanner() {
|
||||
for {
|
||||
m.log.Printf("scanning bounce mailbox %s", m.opt.Mailbox.Host)
|
||||
if err := m.mailbox.Scan(1000, m.queue); err != nil {
|
||||
m.log.Printf("error scanning bounce mailbox: %v", err)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package mailbox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -24,6 +25,15 @@ type bounceHeaders struct {
|
||||
Regexp *regexp.Regexp
|
||||
}
|
||||
|
||||
type bounceMeta struct {
|
||||
From string `json:"from"`
|
||||
Subject string `json:"subject"`
|
||||
MessageID string `json:"message_id"`
|
||||
DeliveredTo string `json:"delivered_to"`
|
||||
Received []string `json:"received"`
|
||||
ClassifyReason string `json:"classify_reason"`
|
||||
}
|
||||
|
||||
var (
|
||||
// List of header to look for in the e-mail body, regexp to fall back to if the header is empty.
|
||||
headerLookups = []bounceHeaders{
|
||||
@@ -37,6 +47,14 @@ var (
|
||||
}
|
||||
|
||||
reHdrReceived = regexp.MustCompile(`(?m)(?:^` + models.EmailHeaderReceived + `:\s+?)(.*)`)
|
||||
|
||||
// SMTP status code (5.x.x or 4.x.x) to classify hard/soft bounces.
|
||||
reSMTPStatus = regexp.MustCompile(`(?m)(?i)^(?:Status:\s*)?(?:\d{3}\s+)?([45]\.\d+\.\d+)`)
|
||||
|
||||
// List of (conventional) strings to guess hard bounces.
|
||||
reHardBounce = regexp.MustCompile(`(?i)(NXDOMAIN|user unknown|address not found|mailbox not found|address.*reject|does not exist|` +
|
||||
`invalid recipient|no such user|recipient.*invalid|undeliverable|permanent.*failure|permanent.*error|` +
|
||||
`bad.*address|unknown.*user|account.*disabled|address.*disabled)`)
|
||||
)
|
||||
|
||||
// NewPOP returns a new instance of the POP mailbox client.
|
||||
@@ -52,6 +70,38 @@ func NewPOP(opt Opt) *POP {
|
||||
}
|
||||
}
|
||||
|
||||
// classifyBounce analyzes the bounce message content and determines if it's a hard or soft bounce.
|
||||
// It checks SMTP status codes, diagnostic headers, and bounce keywords (using string heuristics).
|
||||
// soft is the default preference.
|
||||
// Returns the bounce type and a classification reason containing context about what matched.
|
||||
func classifyBounce(b []byte) (string, string) {
|
||||
if matches := reSMTPStatus.FindAllSubmatch(b, -1); matches != nil {
|
||||
for _, m := range matches {
|
||||
if len(m) >= 2 && len(m[0]) > 1 {
|
||||
// Full status code (e.g., "5.1.1").
|
||||
status := m[1]
|
||||
|
||||
// 5.x.x is hard bounce.
|
||||
if status[0] == '5' {
|
||||
return models.BounceTypeHard, fmt.Sprintf("smtp_status=%s", status)
|
||||
}
|
||||
|
||||
// 4.x.x is soft bounce.
|
||||
if status[0] == '4' {
|
||||
return models.BounceTypeSoft, fmt.Sprintf("smtp_status=%s", status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for explicit hard bounce keywords.
|
||||
if match := reHardBounce.FindSubmatch(b); match != nil {
|
||||
return models.BounceTypeHard, fmt.Sprintf("body_match=%s", match[1])
|
||||
}
|
||||
|
||||
return models.BounceTypeSoft, "default"
|
||||
}
|
||||
|
||||
// Scan scans the mailbox and pushes the downloaded messages into the given channel.
|
||||
// The messages that are downloaded are deleted from the server. If limit > 0,
|
||||
// all messages on the server are downloaded and deleted.
|
||||
@@ -147,24 +197,23 @@ func (p *POP) Scan(limit int, ch chan models.Bounce) error {
|
||||
date = time.Now()
|
||||
}
|
||||
|
||||
// Classify the bounce type based on message content.
|
||||
bounceType, bounceReason := classifyBounce(b.Bytes())
|
||||
|
||||
// Additional bounce e-mail metadata.
|
||||
meta, _ := json.Marshal(struct {
|
||||
From string `json:"from"`
|
||||
Subject string `json:"subject"`
|
||||
MessageID string `json:"message_id"`
|
||||
DeliveredTo string `json:"delivered_to"`
|
||||
Received []string `json:"received"`
|
||||
}{
|
||||
From: hdr[models.EmailHeaderFrom],
|
||||
Subject: hdr[models.EmailHeaderSubject],
|
||||
MessageID: hdr[models.EmailHeaderMessageId],
|
||||
DeliveredTo: hdr[models.EmailHeaderDeliveredTo],
|
||||
Received: msgReceived,
|
||||
fmt.Println(bounceReason)
|
||||
meta, _ := json.Marshal(bounceMeta{
|
||||
From: hdr[models.EmailHeaderFrom],
|
||||
Subject: hdr[models.EmailHeaderSubject],
|
||||
MessageID: hdr[models.EmailHeaderMessageId],
|
||||
DeliveredTo: hdr[models.EmailHeaderDeliveredTo],
|
||||
Received: msgReceived,
|
||||
ClassifyReason: bounceReason,
|
||||
})
|
||||
|
||||
select {
|
||||
case ch <- models.Bounce{
|
||||
Type: "hard",
|
||||
Type: bounceType,
|
||||
CampaignUUID: hdr[models.EmailHeaderCampaignUUID],
|
||||
SubscriberUUID: hdr[models.EmailHeaderSubscriberUUID],
|
||||
Source: p.opt.Host,
|
||||
|
||||