diff --git a/ROADMAP.md b/ROADMAP.md
index 1fde191e..ed05d5e2 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -6,8 +6,8 @@ A high-performance, omni-channel, self-hosted customer support desk.
## Near Term
- OAuth inbox support for Microsoft accounts - Done
- Draft saving support for conversations - Done
-- Support for mentions - WIP
-- Ability to import agents in bulk - WIP
+- Support for mentions - Done
+- Ability to import agents in bulk - Done
## Mid Term
- Full-fledged live chat widget - WIP
diff --git a/cmd/inboxes.go b/cmd/inboxes.go
index 9a92eaf8..86897738 100644
--- a/cmd/inboxes.go
+++ b/cmd/inboxes.go
@@ -4,8 +4,10 @@ import (
"encoding/json"
"net/mail"
"strconv"
+ "strings"
"github.com/abhinavxd/libredesk/internal/envelope"
+ "github.com/abhinavxd/libredesk/internal/inbox"
"github.com/abhinavxd/libredesk/internal/inbox/channel/email/oauth"
"github.com/abhinavxd/libredesk/internal/inbox/channel/livechat"
imodels "github.com/abhinavxd/libredesk/internal/inbox/models"
@@ -56,6 +58,11 @@ func handleCreateInbox(r *fastglue.Request) error {
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), err.Error(), envelope.InputError)
}
+ // Trim whitespace from inbox fields and config.
+ if err := trimInboxFields(&inbox); err != nil {
+ return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "config"), err.Error(), envelope.InputError)
+ }
+
createdInbox, err := app.inbox.Create(inbox)
if err != nil {
return sendErrorEnvelope(r, err)
@@ -94,6 +101,11 @@ func handleUpdateInbox(r *fastglue.Request) error {
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), err.Error(), envelope.InputError)
}
+ // Trim whitespace from inbox fields and config.
+ if err := trimInboxFields(&inbox); err != nil {
+ return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "config"), err.Error(), envelope.InputError)
+ }
+
if err := validateInbox(app, inbox); err != nil {
return sendErrorEnvelope(r, err)
}
@@ -278,3 +290,49 @@ func validateEmailConfig(app *App, configJSON json.RawMessage) error {
return nil
}
+
+// trimInboxFields trims whitespace from inbox fields and its email config if applicable.
+func trimInboxFields(inb *imodels.Inbox) error {
+ inb.Name = strings.TrimSpace(inb.Name)
+ inb.From = strings.TrimSpace(inb.From)
+
+ // Trim email config fields if this is an email channel.
+ if inb.Channel == inbox.ChannelEmail && len(inb.Config) > 0 {
+ var cfg imodels.Config
+ if err := json.Unmarshal(inb.Config, &cfg); err != nil {
+ return err
+ }
+ trimEmailConfig(&cfg)
+ trimmedConfig, err := json.Marshal(cfg)
+ if err != nil {
+ return err
+ }
+ inb.Config = trimmedConfig
+ }
+ return nil
+}
+
+// trimEmailConfig trims whitespace from email configuration fields.
+// Passwords and secrets are intentionally NOT trimmed.
+func trimEmailConfig(cfg *imodels.Config) {
+ // Trim IMAP configs.
+ for i := range cfg.IMAP {
+ cfg.IMAP[i].Host = strings.TrimSpace(cfg.IMAP[i].Host)
+ cfg.IMAP[i].Username = strings.TrimSpace(cfg.IMAP[i].Username)
+ cfg.IMAP[i].Mailbox = strings.TrimSpace(cfg.IMAP[i].Mailbox)
+ }
+
+ // Trim SMTP configs.
+ for i := range cfg.SMTP {
+ cfg.SMTP[i].Host = strings.TrimSpace(cfg.SMTP[i].Host)
+ cfg.SMTP[i].Username = strings.TrimSpace(cfg.SMTP[i].Username)
+ cfg.SMTP[i].HelloHostname = strings.TrimSpace(cfg.SMTP[i].HelloHostname)
+ }
+
+ // Trim OAuth config.
+ if cfg.OAuth != nil {
+ cfg.OAuth.Provider = strings.TrimSpace(cfg.OAuth.Provider)
+ cfg.OAuth.ClientID = strings.TrimSpace(cfg.OAuth.ClientID)
+ cfg.OAuth.TenantID = strings.TrimSpace(cfg.OAuth.TenantID)
+ }
+}
diff --git a/cmd/oauth.go b/cmd/oauth.go
index e3dc9984..ca09a047 100644
--- a/cmd/oauth.go
+++ b/cmd/oauth.go
@@ -152,9 +152,9 @@ func handleOAuthCallback(r *fastglue.Request) error {
redirectURI := oauthData["redirect_uri"]
clientID := oauthData["client_id"]
clientSecret := oauthData["client_secret"]
- tenantID := oauthData["tenant_id"] // Empty string if not set
- flowType := oauthData["flow_type"] // "new_inbox" or "reconnect"
- inboxIDStr := oauthData["inbox_id"] // Inbox ID for reconnect flow
+ tenantID := oauthData["tenant_id"] // Empty string if not set
+ flowType := oauthData["flow_type"] // "new_inbox" or "reconnect"
+ inboxIDStr := oauthData["inbox_id"] // Inbox ID for reconnect flow
// Validate provider matches URL parameter
if storedProvider != provider {
@@ -310,11 +310,12 @@ func handleOAuthCallback(r *fastglue.Request) error {
// Create inbox config
config := imodels.Config{
- SMTP: []imodels.SMTPConfig{smtpConfig},
- IMAP: []imodels.IMAPConfig{imapConfig},
- From: userEmail,
- AuthType: imodels.AuthTypeOAuth2,
- OAuth: oauthConfig,
+ SMTP: []imodels.SMTPConfig{smtpConfig},
+ IMAP: []imodels.IMAPConfig{imapConfig},
+ From: userEmail,
+ AuthType: imodels.AuthTypeOAuth2,
+ OAuth: oauthConfig,
+ EnablePlusAddressing: true,
}
configJSON, err := json.Marshal(config)
diff --git a/cmd/settings.go b/cmd/settings.go
index 264c4997..4182a082 100644
--- a/cmd/settings.go
+++ b/cmd/settings.go
@@ -47,14 +47,19 @@ func handleUpdateGeneralSettings(r *fastglue.Request) error {
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("globals.messages.badRequest"), nil, envelope.InputError)
}
+ // Trim whitespace from string fields.
+ req.SiteName = strings.TrimSpace(req.SiteName)
+ req.FaviconURL = strings.TrimSpace(req.FaviconURL)
+ req.LogoURL = strings.TrimSpace(req.LogoURL)
+ req.Timezone = strings.TrimSpace(req.Timezone)
+ // Trim whitespace and trailing slash from root URL.
+ req.RootURL = strings.TrimRight(strings.TrimSpace(req.RootURL), "/")
+
// Get current language before update.
app.Lock()
oldLang := ko.String("app.lang")
app.Unlock()
- // Remove any trailing slash `/` from the root url.
- req.RootURL = strings.TrimRight(req.RootURL, "/")
-
if err := app.setting.Update(req); err != nil {
return sendErrorEnvelope(r, err)
}
@@ -113,6 +118,14 @@ func handleUpdateEmailNotificationSettings(r *fastglue.Request) error {
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("globals.messages.badRequest"), nil, envelope.InputError)
}
+ // Trim whitespace from string fields (Password intentionally NOT trimmed).
+ req.Host = strings.TrimSpace(req.Host)
+ req.Username = strings.TrimSpace(req.Username)
+ req.EmailAddress = strings.TrimSpace(req.EmailAddress)
+ req.HelloHostname = strings.TrimSpace(req.HelloHostname)
+ req.IdleTimeout = strings.TrimSpace(req.IdleTimeout)
+ req.WaitTimeout = strings.TrimSpace(req.WaitTimeout)
+
out, err := app.setting.GetByPrefix("notification.email")
if err != nil {
return sendErrorEnvelope(r, err)
diff --git a/cmd/upgrade.go b/cmd/upgrade.go
index c2e0c8bb..7514d9eb 100644
--- a/cmd/upgrade.go
+++ b/cmd/upgrade.go
@@ -39,7 +39,8 @@ var migList = []migFunc{
{"v0.8.5", migrations.V0_8_5},
{"v0.9.1", migrations.V0_9_1},
{"v0.10.0", migrations.V0_10_0},
- {"v0.12.0", migrations.V0_12_0},
+ {"v1.0.1", migrations.V1_0_1},
+ {"v2.0.0", migrations.V2_0_0},
}
// upgrade upgrades the database to the current version by running SQL migration files
diff --git a/config.sample.toml b/config.sample.toml
index 55c2b603..46b8150f 100644
--- a/config.sample.toml
+++ b/config.sample.toml
@@ -23,8 +23,8 @@ write_timeout = "5s"
# Maximum request body size in bytes (100MB)
# If you are using proxy, you may need to configure them to allow larger request bodies.
max_body_size = 104857600
-# Size of the read buffer for incoming requests
-read_buffer_size = 4096
+# Size of the read buffer for incoming requests (also limits max header size).
+read_buffer_size = 65536
# Keepalive settings.
keepalive_timeout = "10s"
diff --git a/frontend/apps/main/src/features/admin/general/GeneralSettingForm.vue b/frontend/apps/main/src/features/admin/general/GeneralSettingForm.vue
index 111b6c59..2a1e41d9 100644
--- a/frontend/apps/main/src/features/admin/general/GeneralSettingForm.vue
+++ b/frontend/apps/main/src/features/admin/general/GeneralSettingForm.vue
@@ -30,6 +30,7 @@
French
Italian
Japanese
+ Spanish
Marathi
diff --git a/frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue b/frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue
index 66bdfd95..401e5731 100644
--- a/frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue
+++ b/frontend/apps/main/src/features/admin/inbox/EmailInboxForm.vue
@@ -54,11 +54,25 @@
-
+
{{ $t('admin.inbox.csatSurveys.description_2') }}
+
+
+
+ {{ $t('admin.inbox.enablePlusAddressing') }}
+
+ {{ $t('admin.inbox.enablePlusAddressing.description') }}
+
+
+
+
+
+
+
+
@@ -729,6 +743,7 @@ const form = useForm({
from: '',
enabled: true,
csat_enabled: false,
+ enable_plus_addressing: true,
auth_type: AUTH_TYPE_PASSWORD,
imap: {
host: 'imap.gmail.com',
diff --git a/frontend/apps/main/src/features/admin/inbox/formSchema.js b/frontend/apps/main/src/features/admin/inbox/formSchema.js
index 9ddc4b4e..d91f00fd 100644
--- a/frontend/apps/main/src/features/admin/inbox/formSchema.js
+++ b/frontend/apps/main/src/features/admin/inbox/formSchema.js
@@ -7,6 +7,7 @@ export const createFormSchema = (t) => z.object({
from: z.string().min(1, t('globals.messages.required')),
enabled: z.boolean().optional(),
csat_enabled: z.boolean().optional(),
+ enable_plus_addressing: z.boolean().optional(),
auth_type: z.enum([AUTH_TYPE_PASSWORD, AUTH_TYPE_OAUTH2]),
oauth: z.object({
access_token: z.string().optional(),
diff --git a/frontend/apps/main/src/views/admin/inbox/EditInbox.vue b/frontend/apps/main/src/views/admin/inbox/EditInbox.vue
index 04f08029..e3179e43 100644
--- a/frontend/apps/main/src/views/admin/inbox/EditInbox.vue
+++ b/frontend/apps/main/src/views/admin/inbox/EditInbox.vue
@@ -44,16 +44,15 @@ const breadcrumbLinks = [
const submitForm = (values) => {
let payload
-
+
if (inbox.value.channel === 'email') {
- // Prepare request payload from form values
const config = {
auth_type: values.auth_type,
+ enable_plus_addressing: values.enable_plus_addressing,
imap: [{ ...values.imap }],
smtp: [{ ...values.smtp }]
}
- // Only add oauth if auth_type is oauth2
if (values.auth_type === AUTH_TYPE_OAUTH2) {
config.oauth = values.oauth
}
@@ -64,7 +63,6 @@ const submitForm = (values) => {
config
}
- // Set dummy passwords to empty string
if (payload.config.imap[0].password?.includes('•')) {
payload.config.imap[0].password = ''
}
@@ -130,6 +128,7 @@ onMounted(async () => {
}
inboxData.auth_type = inboxData?.config?.auth_type || AUTH_TYPE_PASSWORD
inboxData.oauth = inboxData?.config?.oauth || {}
+ inboxData.enable_plus_addressing = inboxData?.config?.enable_plus_addressing || false
inbox.value = inboxData
} catch (error) {
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
diff --git a/frontend/apps/main/src/views/admin/inbox/NewInbox.vue b/frontend/apps/main/src/views/admin/inbox/NewInbox.vue
index 5cddcbc0..84a12e5a 100644
--- a/frontend/apps/main/src/views/admin/inbox/NewInbox.vue
+++ b/frontend/apps/main/src/views/admin/inbox/NewInbox.vue
@@ -173,6 +173,7 @@ const submitForm = (values) => {
from: values.from,
channel: channelName,
config: {
+ enable_plus_addressing: values.enable_plus_addressing,
imap: [values.imap],
smtp: [values.smtp]
}
diff --git a/i18n/da.json b/i18n/da.json
index 3eab0df5..bfbbd76b 100644
--- a/i18n/da.json
+++ b/i18n/da.json
@@ -35,7 +35,6 @@
"globals.terms.dashboard": "Kontrolpanel | Kontrolpaneler",
"globals.terms.tag": "Tag | Tags",
"globals.terms.sla": "SLA | SLA'er",
- "globals.terms.slaPolicy": "SLA-politik | SLA-politikker",
"globals.terms.csatSurvey": "CSAT-undersøgelse | CSAT-undersøgelser",
"globals.terms.csatResponse": "CSAT-svar | CSAT-svar",
"globals.terms.inbox": "Indbakke | Indbakker",
@@ -389,10 +388,8 @@
"admin.general.logoURL": "Logo-URL",
"admin.general.logoURL.description": "Logo-URL til appen.",
"admin.general.logoURL.valid": "Logo-URL skal være en gyldig URL",
- "admin.general.maxAllowedFileUploadSize": "Maks. tilladt filstørrelse til upload",
"admin.general.maxAllowedFileUploadSize.description": "Maks. tilladt filstørrelse til upload i MB.",
"admin.general.maxAllowedFileUploadSize.valid": "Maks. tilladt filstørrelse til upload skal være mellem 1 og 500 MB",
- "admin.general.allowedFileUploadExtensions": "Tilladte filtyper til upload",
"admin.general.allowedFileUploadExtensions.description": "Benyt `*` for at tillade alle filtyper, f.eks. `jpg, png, pdf`",
"admin.businessHours.unauthorized": "Manglende rettighed til at få vist åbningstider.",
"admin.businessHours.setBusinessHours": "Indstil åbningstider",
@@ -556,7 +553,6 @@
"report.chart.newConversations": "Nye samtaler",
"report.chart.resolvedConversations": "Løste samtaler",
"report.chart.title": "Samtaletendenser",
- "report.sla.cardTitle": "SLA-ydeevne (seneste {days} dage)",
"report.sla.firstRespMet": "Første svar opfyldt",
"report.sla.firstRespBreached": "Første svar brudt",
"report.sla.avgFirstResp": "Gns. første svartid",
diff --git a/i18n/de.json b/i18n/de.json
index 50b910e0..a7b3fccc 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -3,12 +3,12 @@
"_.name": "Deutsch (de)",
"globals.terms.user": "Benutzer | Benutzer",
"globals.terms.contact": "Kontakt | Kontakte",
- "globals.terms.agent": "Agent | Mitarbeiter",
+ "globals.terms.agent": "Mitarbeiter | Mitarbeiter",
"globals.terms.team": "Team | Teams",
"globals.terms.message": "Nachricht | Nachrichten",
"globals.terms.activityMessage": "Aktivitätsnachricht | Aktivitätsnachrichten",
"globals.terms.account": "Konto | Konten",
- "globals.terms.conversation": "Unterhaltung | Unterhaltungen",
+ "globals.terms.conversation": "Konversation | Konversationen",
"globals.terms.provider": "Anbieter | Anbieter",
"globals.terms.state": "Bundesland | Bundesländer",
"globals.terms.webhook": "Webhook | Webhooks",
@@ -26,7 +26,7 @@
"globals.terms.setting": "Einstellung | Einstellungen",
"globals.terms.template": "Vorlage | Vorlagen",
"globals.terms.rule": "Regel | Regeln",
- "globals.terms.businessHour": "Geschäftszeit | Geschäftszeiten",
+ "globals.terms.businessHour": "Öffnungszeiten | Öffnungszeiten",
"globals.terms.priority": "Priorität | Prioritäten",
"globals.terms.status": "Status | Status",
"globals.terms.secret": "Geheimnis | Geheimnisse",
@@ -54,9 +54,10 @@
"globals.terms.role": "Rolle | Rollen",
"globals.terms.avatar": "Profilbild | Profilbilder",
"globals.terms.view": "Ansicht | Ansichten",
+ "globals.terms.sharedView": "Geteilte Ansicht | Geteilte Ansichten",
"globals.terms.email": "E-Mail | E-Mails",
"globals.terms.condition": "Bedingung | Bedingungen",
- "globals.terms.sso": " | ",
+ "globals.terms.sso": "SSO | SSOs",
"globals.terms.hour": "Stunde | Stunden",
"globals.terms.day": "Tag | Tage",
"globals.terms.filter": "Filter | Filter",
@@ -82,10 +83,11 @@
"globals.terms.open": "Offen",
"globals.terms.awaitingResponse": "Wartend auf Rückmeldung",
"globals.terms.unassigned": "Nicht zugewiesen",
+ "globals.terms.mention": "Erwähnung | Erwähnungen",
"globals.terms.pending": "Ausstehend",
"globals.terms.active": "Aktiv",
"globals.terms.url": "URL | URLs",
- "globals.terms.rootURL": "Stamm-URL",
+ "globals.terms.rootURL": "Basis-URL",
"globals.terms.key": "Schlüssel | Schlüssel",
"globals.terms.note": "Notiz | Notizen",
"globals.terms.ipAddress": "IP-Adresse | IP-Adressen",
@@ -101,7 +103,7 @@
"globals.terms.notification": "Benachrichtigung | Benachrichtigungen",
"globals.terms.security": "Sicherheit | Sicherheit",
"globals.terms.myInbox": "Mein Posteingang | Meine Posteingänge",
- "globals.terms.teamInbox": "Team Posteingang | Team Posteingänge",
+ "globals.terms.teamInbox": "Team-Posteingang | Team-Posteingänge",
"globals.terms.optional": "Optional | Optional",
"globals.terms.visibility": "Sichtbarkeit | Sichtbarkeit",
"globals.terms.privateNote": "Private Notiz | Private Notizen",
@@ -135,7 +137,7 @@
"globals.terms.confirmation": "Bestätigung | Bestätigungen",
"globals.terms.dialog": "Dialog | Dialoge",
"globals.terms.modal": "Modal | Modals",
- "globals.terms.timezone": "Zeitzonen | Zeitzonen",
+ "globals.terms.timezone": "Zeitzone | Zeitzonen",
"globals.terms.language": "Sprache | Sprachen",
"globals.terms.regex": "Regex | Regexes",
"globals.terms.appliesTo": "Gilt für",
@@ -256,7 +258,7 @@
"globals.messages.noResults": "Keine {name} gefunden",
"globals.messages.enter": "{name} eingeben",
"globals.messages.yes": "Ja {name}",
- "globals.messages.no": "No {name}",
+ "globals.messages.no": "Nein {name}",
"globals.messages.select": "{name} auswählen",
"globals.messages.copied": "In die Zwischenablage kopiert",
"globals.messages.search": "{name} suchen",
@@ -410,10 +412,10 @@
"admin.general.logoURL": "Logo URL",
"admin.general.logoURL.description": "Logo URL für die App.",
"admin.general.logoURL.valid": "Logo URL muss eine gültige URL sein",
- "admin.general.maxAllowedFileUploadSize": "Maximal erlaubte Datei-Upload-Größe",
- "admin.general.maxAllowedFileUploadSize.description": "Maximal erlaubte Datei-Upload-Größe in MB.",
- "admin.general.maxAllowedFileUploadSize.valid": "Maximal erlaubte Datei-Upload-Größe sollte zwischen 1 und 500 MB liegen",
- "admin.general.allowedFileUploadExtensions": "Erlaubte Datei-Upload-Erweiterungen",
+ "admin.general.maxAllowedFileUploadSize": "Maximal erlaubte Größe für Datei-Uploads",
+ "admin.general.maxAllowedFileUploadSize.description": "Maximal erlaubte Größe für Datei-Uploads in MB.",
+ "admin.general.maxAllowedFileUploadSize.valid": "Die maximal erlaubte Größe für Datei-Uploads sollte zwischen 1 und 500 MB liegen",
+ "admin.general.allowedFileUploadExtensions": "Erlaubte Dateiendungen für Uploads",
"admin.general.allowedFileUploadExtensions.description": "Verwende `*` um alle Dateitypen zu erlauben. Zum Beispiel: `jpg, png, pdf`",
"admin.businessHours.unauthorized": "Du hast keine Berechtigung, um Öffnungszeiten anzuzeigen.",
"admin.businessHours.setBusinessHours": "Öffnungszeiten auswählen",
@@ -555,7 +557,7 @@
"admin.automation.invalid": "Stelle sicher, dass du mindestens eine Aktion und eine Regel hast und ihre Werte nicht leer sind.",
"admin.notification.restartApp": "Einstellungen erfolgreich aktualisiert. Bitte starte die App neu, damit die Änderungen wirksam werden.",
"admin.banner.restartMessage": "Einige Einstellungen wurden geändert, die einen Neustart der Anwendung erfordern.",
- "admin.template.outgoingEmailTemplates": "Ausgehende E-Mail Vorlagen",
+ "admin.template.outgoingEmailTemplates": "Vorlagen für ausgehende E-Mails",
"admin.template.emailNotificationTemplates": "Vorlage für E-Mail Benachrichtigungen",
"admin.template.makeSureTemplateHasContent": "Stelle sicher, dass die Vorlage nur einmal {content} enthält.",
"admin.template.onlyOneDefaultOutgoingTemplate": "Du kannst nur eine Standardvorlage für ausgehende E-Mails haben.",
@@ -563,7 +565,7 @@
"admin.customAttributes.regex.description": "Regex um den Wert dieses benutzerdefinierten Attributs zu überprüfen. Leer lassen um die Überprüfung zu überspringen.",
"admin.customAttributes.regexHint.description": "Hinweis für Regex-Muster.",
"admin.customAttributes.keyNotAllowed": "Der angegebene Schlüssel ist nicht zulässig, da er mit den Standardattributen kollidiert. Bitte verwenden Sie einen anderen Schlüssel.",
- "admin.tags.deleteConfirmation": "Sind Sie sicher, dass Sie dieses Tag löschen möchten? Dadurch wird es auch von allen Unterhaltungen entfernt",
+ "admin.tags.deleteConfirmation": "Sind Sie sicher, dass Sie dieses Tag löschen möchten? Dadurch wird es auch von allen Konversationen entfernt",
"command.typeCmdOrSearch": "Gib einen Befehl ein oder suche...",
"command.noCommandAvailable": "Kein Befehl verfügbar",
"command.selectAMacro": "Wähle ein Makro um Details anzuzeigen",
@@ -580,13 +582,30 @@
"report.sla.cardTitle": "SLA-Leistung (Letzte {days} Tage)",
"report.sla.firstRespMet": "Erste Antwort",
"report.sla.firstRespBreached": "Erste Antwort verletzt",
- "report.sla.avgFirstResp": "Durchschnittliche erste Antwortzeit",
+ "report.sla.avgFirstResp": "Durchschnittliche Zeit für erste Antwort",
"report.sla.nextRespMet": "Nächste Antwort",
"report.sla.nextRespBreached": "Nächste Antwort verletzt",
"report.sla.avgNextResp": "Durchschnittliche Antwortzeit für die nächste",
"report.sla.resolutionMet": "Lösung Erfüllt",
"report.sla.resolutionBreached": "Lösung verletzt",
"report.sla.avgResolution": "Durchschnittliche Lösungszeit",
+ "report.openConversations": "Offene Konversationen",
+ "report.agentStatus": "Mitarbeiterstatus",
+ "report.csat.title": "Kundenzufriedenheit",
+ "report.csat.cardTitle": "Kundenzufriedenheit (letzte {days} Tage)",
+ "report.csat.avgRating": "Durchschnittliche Bewertung",
+ "report.csat.responseRate": "Antwortquote",
+ "report.csat.responses": "Antworten",
+ "report.messages.title": "Nachrichtenvolumen",
+ "report.messages.cardTitle": "Nachrichtenvolumen (letzte {days} Tage)",
+ "report.messages.total": "Gesamt",
+ "report.messages.incoming": "Eingehend",
+ "report.messages.outgoing": "Ausgehend",
+ "report.messages.perConversation": "pro Konversation",
+ "report.tags.title": "Tag-Verteilung",
+ "report.tags.cardTitle": "Tag-Verteilung (letzte {days} Tage)",
+ "report.tags.tagged": "Getaggt",
+ "report.tags.topTags": "Top-Tags",
"search.noResultsForQuery": "Keine Ergebnisse für die Abfrage `{query}` gefunden. Versuche einen anderen Suchbegriff.",
"search.minQueryLength": "Bitte gib mindestens {length} Zeichen ein, um zu suchen.",
"search.searchBy": "Suche nach Referenznummer, Kontakt-E-Mail-Adresse oder Nachrichten in Konversationen.",
@@ -605,32 +624,32 @@
"account.removeAvatar": "Profilbild entfernen",
"account.cropAvatar": "Profilbild zuschneiden",
"account.avatarRemoved": "Profilbild entfernt",
- "conversation.resolveWithoutAssignee": "Die Unterhaltung kann ohne einen zugewiesenen Benutzer nicht abgeschlossen werden. Bitte weise vor dem Abschließen einen Benutzer zu",
+ "conversation.resolveWithoutAssignee": "Die Konversation kann ohne einen zugewiesenen Benutzer nicht abgeschlossen werden. Bitte weise vor dem Abschließen einen Benutzer zu",
"conversation.notMemberOfTeam": "Du bist kein Mitglied dieses Teams. Bitte aktualisiere die Seite und versuche es erneut",
"conversation.viewPermissionDenied": "Du hast keinen Zugriff auf diese Ansicht",
"conversation.errorGeneratingMessageID": "Fehler beim Generieren der Nachrichten-ID",
"conversation.invalidSnoozeDuration": "Ungültige Schlummerdauer",
"conversation.errorUnassigningOpenConversations": "Fehler beim Aufheben der Zuweisung der offenen Konversationen",
"conversation.errorRemovingConversationAssignee": "Fehler beim Entfernen des zugewiesenen Mitarbeiters",
- "conversation.placeholder": "Wähle eine Konversation vom linken Fenster.",
+ "conversation.placeholder": "Wähle eine Konversation aus dem linken Bereich aus.",
"conversation.searchContact": "Kontakt per E-Mail suchen oder neue E-Mail eingeben",
"conversation.sort.oldestActivity": "Älteste Aktivität",
- "conversation.sort.newestActivity": "Älteste Aktivität",
+ "conversation.sort.newestActivity": "Letzte Aktivität",
"conversation.sort.startedFirst": "Zuerst begonnen",
"conversation.sort.startedLast": "Zuletzt gestartet",
"conversation.sort.waitingLongest": "Am längsten wartend",
"conversation.sort.nextSLATarget": "Nächster SLA Ablauf",
"conversation.sort.priorityFirst": "Priorisierte zuerst",
- "conversation.noConversationsFound": "Keine Unterhaltungen gefunden",
+ "conversation.noConversationsFound": "Keine Konversationen gefunden",
"conversation.tryAdjustingFilters": "Versuche, die Filter anzupassen",
- "conversation.couldNotFetch": "Unterhaltungen konnten nicht abgerufen werden",
- "conversation.allLoaded": "Alle Unterhaltungen geladen",
+ "conversation.couldNotFetch": "Konversationen konnten nicht abgerufen werden",
+ "conversation.allLoaded": "Alle Konversationen geladen",
"conversation.showQuotedText": "Zitierten Text anzeigen",
"conversation.hideQuotedText": "Zitierten Text ausblenden",
"conversation.sidebar.information": "Information",
"conversation.sidebar.contactAttributes": "Kontaktattribute",
- "conversation.sidebar.previousConvo": "Vorherige Unterhaltungen",
- "conversation.sidebar.noPreviousConvo": "Keine vorherigen Unterhaltungen",
+ "conversation.sidebar.previousConvo": "Vorherige Konversationen",
+ "conversation.sidebar.noPreviousConvo": "Keine vorherigen Konversationen",
"conversation.sidebar.notAvailable": "Nicht verfügbar",
"editor.newLine": "Shift + Enter um eine neue Zeile hinzuzufügen. ",
"editor.send": " Strg + Enter zum senden. ",
diff --git a/i18n/en.json b/i18n/en.json
index ca18ea5d..a099dd0e 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -559,6 +559,8 @@
"admin.inbox.heloHostname.description": "The hostname to use in the HELO/EHLO command. If not set, defaults to localhost.",
"admin.inbox.skipTLSVerification": "Skip TLS Verification",
"admin.inbox.skipTLSVerification.description": "Skip hostname check on the TLS certificate.",
+ "admin.inbox.enablePlusAddressing": "Enable plus addressing",
+ "admin.inbox.enablePlusAddressing.description": "Improves conversation threading but requires provider support (e.g., Gmail, Microsoft 365).",
"admin.inbox.chooseChannel": "Choose a channel",
"admin.inbox.configureChannel": "Configure channel",
"admin.inbox.createEmailInbox": "Create Email Inbox",
diff --git a/i18n/es.json b/i18n/es.json
new file mode 100644
index 00000000..babba271
--- /dev/null
+++ b/i18n/es.json
@@ -0,0 +1,150 @@
+{
+ "_.code": "en",
+ "_.name": "Inglés (en)",
+ "globals.terms.user": "Usuario | Usuarios",
+ "globals.terms.contact": "Contacto | Contactos",
+ "globals.terms.agent": "Agente | Agentes",
+ "globals.terms.team": "Equipo | Equipos",
+ "globals.terms.message": "Mensaje | Mensajes",
+ "globals.terms.activityMessage": "Actualización de actividad | Actualización de actividades",
+ "globals.terms.account": "Cuenta | Cuentas",
+ "globals.terms.conversation": "Conversación | Conversaciones",
+ "globals.terms.provider": "Proveedor | Proveedores",
+ "globals.terms.state": "Estado | Estados",
+ "globals.terms.webhook": "Web Hook | Hooks web",
+ "globals.terms.session": "Sesión | Sesiones",
+ "globals.terms.media": "Medio | Medios",
+ "globals.terms.permission": "Permiso | Permisos",
+ "globals.terms.request": "Solicitud | Solicitudes",
+ "globals.terms.file": "Archivo | Archivos",
+ "globals.terms.actor": "Actor | Actores",
+ "globals.terms.page": "Página | Páginas",
+ "globals.terms.activityLog": "Historial de actividad | Historial de actividades",
+ "globals.terms.name": "Nombre | Nombres",
+ "globals.terms.image": "Imagen | Imágenes",
+ "globals.terms.thumbnail": "Vista en miniatura | Vistas en miniatura",
+ "globals.terms.setting": "Ajuste | Ajustes",
+ "globals.terms.template": "Plantilla | Plantillas",
+ "globals.terms.rule": "Regla | Reglas",
+ "globals.terms.businessHour": "Horario de atención | Horario de trabajo",
+ "globals.terms.priority": "Prioridad | Prioridades",
+ "globals.terms.status": "Estado | Estados",
+ "globals.terms.secret": "Secreto | Secretos",
+ "globals.terms.inactive": "Inactivo | Inactivos",
+ "globals.terms.integration": "Integración | Integraciones",
+ "globals.terms.content": "Contenido | Contenidos",
+ "globals.terms.appRootURL": "URL raíz de la aplicación",
+ "globals.terms.dashboard": "Tablero | Tableros",
+ "globals.terms.tag": "Etiqueta | Etiquetas",
+ "globals.terms.sla": "Acuerdo de Nivel de Servicio | Acuerdos de Nivel de Servicio",
+ "globals.terms.csatSurvey": "Encuesta CSAT | Encuestas CSAT",
+ "globals.terms.csatResponse": "Repuesta CSAT | Respuestas CSAT",
+ "globals.terms.inbox": "Bandeja de entrada | Bandejas de entrada",
+ "globals.terms.conversationParticipant": "Participante de la conversación | Participantes de la conversación",
+ "globals.terms.config": "Configuración | Configuraciones",
+ "globals.terms.macro": "Macro | Macros",
+ "globals.terms.macroAction": "Acción de macro | Acciones de macro",
+ "globals.terms.action": "Acción | Acciones",
+ "globals.terms.value": "Valor | Valores",
+ "globals.terms.event": "Evento | Eventos",
+ "globals.terms.automation": "Automatización | Automatizaciones",
+ "globals.terms.oidc": "OIDC | OIDCs",
+ "globals.terms.oidcProvider": "Proveedor OIDC | Proveedores OIDC",
+ "globals.terms.role": "Rol | Roles",
+ "globals.terms.avatar": "Avatar | Avatares",
+ "globals.terms.view": "Vista | Vistas",
+ "globals.terms.email": "Correo Electrónico | Correos Electrónicos",
+ "globals.terms.condition": "Condición | Condiciones",
+ "globals.terms.sso": "SSO | SSO",
+ "globals.terms.hour": "Hora | Horas",
+ "globals.terms.day": "Día | Días",
+ "globals.terms.filter": "Filtro | Filtros",
+ "globals.terms.profile": "Perfil | Perfiles",
+ "globals.terms.apiKey": "Llave API | Llaves API",
+ "globals.terms.loading": "Cargando...",
+ "globals.terms.loadMore": "Cargar Más",
+ "globals.terms.holiday": "Festivo | Vacaciones",
+ "globals.terms.password": "Contraseña | Contraseñas",
+ "globals.terms.result": "Resultado | Resultados",
+ "globals.terms.meta": "Meta | Metas",
+ "globals.terms.online": "En línea | En línea",
+ "globals.terms.offline": "Desconectado | Desconectado",
+ "globals.terms.away": "Ausente | Ausente",
+ "globals.terms.admin": "Administrador | Administradores",
+ "globals.terms.customAttribute": "Atributo personalizado | Atributos personalizados",
+ "globals.terms.attribute": "Atributo | Atributo",
+ "globals.terms.tryAgain": "Intenta de nuevo",
+ "globals.terms.search": "Búsqueda",
+ "globals.terms.live": "En vivo",
+ "globals.terms.lastUpdated": "Última actualización",
+ "globals.terms.total": "Total",
+ "globals.terms.open": "Abrir",
+ "globals.terms.awaitingResponse": "Esperando respuesta",
+ "globals.terms.unassigned": "No asignado",
+ "globals.terms.pending": "Pendiente",
+ "globals.terms.active": "Activo",
+ "globals.terms.url": "URL | URLs",
+ "globals.terms.rootURL": "URL raíz",
+ "globals.terms.key": "Clave | Claves",
+ "globals.terms.note": "Nota | Notas",
+ "globals.terms.ipAddress": "Dirección IP | Direcciones IP",
+ "globals.terms.alert": "Alerta | Alertas",
+ "globals.terms.duration": "Duración | Duraciones",
+ "globals.terms.slaMetric": "Metrica SLA | Metricas SLA ",
+ "globals.terms.general": "General",
+ "globals.terms.optional": "Opcional | Opcionales",
+ "globals.terms.visibility": "Visibilidad | Visibilidades",
+ "globals.terms.privateNote": "Nota privada | Notas privadas",
+ "globals.terms.automationRule": "Regla de automatización | Reglas de automatización",
+ "globals.terms.subject": "Asunto | Asuntos",
+ "globals.terms.today": "Hoy",
+ "globals.terms.csat": "CSAT | CSATs",
+ "globals.terms.field": "Campo | Campos",
+ "globals.terms.column": "Columna | Columnas",
+ "globals.terms.row": "Fila | Filas",
+ "globals.terms.button": "Botón | Botones",
+ "globals.terms.link": "Vínculo | Vínculos",
+ "globals.terms.icon": "Icono | Iconos",
+ "globals.terms.error": "Error | Errores",
+ "globals.terms.success": "Éxito | Éxitos",
+ "globals.terms.type": "Tipo | Tipos",
+ "globals.terms.warning": "Advertencia | Advertencias",
+ "globals.terms.info": "Información | Información",
+ "globals.terms.enabled": "Habilitado",
+ "globals.terms.disabled": "Deshabilitado",
+ "globals.terms.required": "Requerido",
+ "globals.terms.min": "Mínimo | Mínimos",
+ "globals.terms.max": "Máximo | Máximos",
+ "globals.terms.import": "Importar | Importar",
+ "globals.terms.export": "Exportar | Exportar",
+ "globals.terms.appliesTo": "Aplica a",
+ "globals.terms.createdOn": "Creado el",
+ "globals.terms.awayReassigning": "Ausente y reasignando",
+ "globals.terms.availabilityStatus": "Estado de disponibilidad",
+ "globals.terms.lastActive": "Última actividad",
+ "globals.terms.lastLogin": "Último acceso",
+ "globals.terms.providerURL": "URL del proveedor",
+ "globals.terms.clientID": "ID del cliente",
+ "globals.terms.clientSecret": "Secreto del cliente",
+ "globals.terms.callbackURL": "URL de retorno (callback)",
+ "globals.terms.referenceNumber": "Número de referencia",
+ "globals.terms.initiatedAt": "Iniciado el",
+ "globals.terms.firstReplyAt": "Primera respuesta el",
+ "globals.terms.lastReplyAt": "Última respuesta el",
+ "globals.terms.resolvedAt": "Resuelto el",
+ "globals.terms.closedAt": "Cerrado el",
+ "globals.terms.isDefault": "Es predeterminado",
+ "globals.terms.body": "Contenido principal",
+ "globals.terms.default": "Predeterminado",
+ "globals.terms.channel": "Canal",
+ "globals.terms.configure": "Configurar",
+ "globals.terms.date": "Fecha",
+ "globals.terms.fromEmailAddress": "Correo electrónico del remitente | Correos electrónicos de los remitentes",
+ "globals.messages.custom": "{name} personalizado",
+ "globals.messages.replying": "Respondiendo",
+ "globals.messages.hoursSince": "Horas desde",
+ "globals.messages.hoursSinceCreated": "Horas desde que se creó",
+ "globals.messages.hoursSinceFirstReply": "Horas desde la primera respuesta",
+ "globals.messages.hoursSinceLastReply": "Horas desde la última respuesta",
+ "globals.messages.hoursSinceResolved": "Horas desde que se resolvió"
+}
\ No newline at end of file
diff --git a/i18n/fa.json b/i18n/fa.json
index 73217a7c..fd0c7ae3 100644
--- a/i18n/fa.json
+++ b/i18n/fa.json
@@ -35,7 +35,6 @@
"globals.terms.dashboard": "داشبورد | داشبوردها",
"globals.terms.tag": "برچسب | برچسبها",
"globals.terms.sla": "SLA | SLAs",
- "globals.terms.slaPolicy": "سیاست SLA | سیاستهای SLA",
"globals.terms.csatSurvey": "نظرسنجی CSAT | نظرسنجیهای CSAT",
"globals.terms.csatResponse": "پاسخ CSAT | پاسخهای CSAT",
"globals.terms.inbox": "صندوق ورودی | صندوقهای ورودی",
@@ -227,10 +226,8 @@
"admin.general.logoURL": "آدرس URL لوگو",
"admin.general.logoURL.description": "آدرس URL لوگو برای اپلیکیشن.",
"admin.general.logoURL.valid": "آدرس URL لوگو باید یک URL معتبر باشد",
- "admin.general.maxAllowedFileUploadSize": "حداکثر اندازه مجاز بارگذاری فایل",
"admin.general.maxAllowedFileUploadSize.description": "حداکثر اندازه مجاز بارگذاری فایل به مگابایت.",
"admin.general.maxAllowedFileUploadSize.valid": "حداکثر اندازه مجاز بارگذاری فایل باید بین 1 و 500 مگابایت باشد",
- "admin.general.allowedFileUploadExtensions": "پسوندهای مجاز بارگذاری فایل",
"admin.businessHours.unauthorized": "شما مجوز مشاهده ساعات کاری را ندارید.",
"admin.businessHours.setBusinessHours": "تنظیم ساعات کاری",
"admin.businessHours.customBusinessHours": "ساعات کاری سفارشی",
diff --git a/i18n/fr.json b/i18n/fr.json
index c33ecfb5..6cde0991 100644
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -37,7 +37,6 @@
"globals.terms.dashboard": "Tableau de bord | Tableaux de bord",
"globals.terms.tag": "Étiquette | Étiquettes",
"globals.terms.sla": "SLA | SLA",
- "globals.terms.slaPolicy": "Politique de SLA | Politiques de SLA",
"globals.terms.csatSurvey": "Enquête de satisfaction | Enquêtes de satisfaction",
"globals.terms.csatResponse": "Réponse à l'enquête de satisfaction | Réponses à l'enquête de satisfaction",
"globals.terms.inbox": "Boîte de réception | Boîtes de réception",
@@ -409,10 +408,8 @@
"admin.general.logoURL": "URL du logo",
"admin.general.logoURL.description": "URL du logo de l'application.",
"admin.general.logoURL.valid": "L'URL du logo doit être valide",
- "admin.general.maxAllowedFileUploadSize": "Taille maximale autorisée pour le téléchargement de fichiers",
"admin.general.maxAllowedFileUploadSize.description": "Taille maximale autorisée pour le téléchargement de fichiers en Mo.",
"admin.general.maxAllowedFileUploadSize.valid": "La taille maximale autorisée pour le téléchargement de fichiers doit être comprise entre 1 et 500 Mo",
- "admin.general.allowedFileUploadExtensions": "Extensions autorisées pour le téléchargement de fichiers",
"admin.general.allowedFileUploadExtensions.description": "Utilisez `*` pour autoriser tous les types de fichiers. Par exemple : `jpg, png, pdf`",
"admin.businessHours.unauthorized": "Vous n'êtes pas autorisé à consulter les heures d'ouverture.",
"admin.businessHours.setBusinessHours": "Fixer les heures d'ouverture",
@@ -494,6 +491,7 @@
"admin.role.conversations.readAll": "Voir toutes les conversations",
"admin.role.conversations.readUnassigned": "Voir toutes les conversations non attribuées",
"admin.role.conversations.readTeamInbox": "Voir les conversations dans la boîte de réception de l'équipe",
+ "admin.role.conversations.readTeamAll": "Voir les conversations de votre équipe",
"admin.role.conversations.updateUserAssignee": "Attribuer des conversations à des utilisateurs",
"admin.role.conversations.updateTeamAssignee": "Affecter des conversations à des équipes",
"admin.role.conversations.updatePriority": "Changer la priorité de la conversation",
@@ -576,7 +574,6 @@
"report.chart.newConversations": "Nouvelles conversations",
"report.chart.resolvedConversations": "Conversations résolues",
"report.chart.title": "Tendances de conversation",
- "report.sla.cardTitle": "Performance du SLA ({days} derniers jours)",
"report.sla.firstRespMet": "Date de première réponse",
"report.sla.firstRespBreached": "Temps Première Réponse",
"report.sla.avgFirstResp": "Durée Moyenne de Première Réponse",
diff --git a/i18n/it.json b/i18n/it.json
index ce82091b..ec4c7e1d 100644
--- a/i18n/it.json
+++ b/i18n/it.json
@@ -8,6 +8,7 @@
"globals.terms.message": "Messaggio | Messaggi",
"globals.terms.activityMessage": "Messaggio attività | Messaggi attività",
"globals.terms.account": "Account | Account",
+ "globals.terms.authorization": "Autorizzazione",
"globals.terms.conversation": "Conversazione | Conversazioni",
"globals.terms.provider": "Fornitore | Fornitori",
"globals.terms.state": "Stato | Stati",
@@ -17,9 +18,18 @@
"globals.terms.permission": "Permesso | Permessi",
"globals.terms.request": "Richiesta | Richieste",
"globals.terms.file": "File | File",
+ "globals.terms.csvFile": "Uno | Altro",
"globals.terms.actor": "Attore | Attori",
"globals.terms.page": "Pagina | Pagine",
+ "globals.terms.log": "Uno | Registri",
"globals.terms.activityLog": "Log Attività | Logs Attività",
+ "activityLog.type.agentLogin": "Accesso agente",
+ "activityLog.type.agentLogout": "Uscita agente",
+ "activityLog.type.agentAway": "Agente assente",
+ "activityLog.type.agentAwayReassigned": "Agente assente riassegnato",
+ "activityLog.type.agentOnline": "Agente online",
+ "activityLog.type.agentPasswordSet": "Password agente impostata",
+ "activityLog.type.agentRolePermissionsChanged": "Permessi del ruolo dell’agente modificati",
"globals.terms.name": "Nome | Nomi",
"globals.terms.image": "Immagine | Immagini",
"globals.terms.thumbnail": "Miniatura | Miniature",
@@ -37,7 +47,7 @@
"globals.terms.dashboard": "Scrivania | Scrivanie",
"globals.terms.tag": "Tag | Tags",
"globals.terms.sla": "SLA | Contratti di servizio",
- "globals.terms.slaPolicy": "Politica di servizio | Polizze di Servizio",
+ "globals.terms.slaPolicy": "Politica SLA | Politiche SLA",
"globals.terms.csatSurvey": "Questionario di soddisfazione | Questionarii di soddisfazione",
"globals.terms.csatResponse": "Risposta | Risposte",
"globals.terms.inbox": "Casella in arrivo | Caselle di posta",
@@ -54,6 +64,7 @@
"globals.terms.role": "Ruolo | Ruoli",
"globals.terms.avatar": "Avatar | Avatar",
"globals.terms.view": "Vista | Viste",
+ "globals.terms.sharedView": "Vista condivisa | Viste condivise",
"globals.terms.email": "Email | Email",
"globals.terms.condition": "Condizione | Condizioni",
"globals.terms.sso": "SSO | SSO",
@@ -82,6 +93,7 @@
"globals.terms.open": "Aperto",
"globals.terms.awaitingResponse": "In attesa di risposta",
"globals.terms.unassigned": "Non assegnato",
+ "globals.terms.mention": "Menzione | Menzioni",
"globals.terms.pending": "In sospeso",
"globals.terms.active": "Attivo",
"globals.terms.url": "URL | URLs",
@@ -126,6 +138,10 @@
"globals.terms.min": "Minimo | Minimi",
"globals.terms.max": "Massimo | Massimi",
"globals.terms.length": "Lunghezza | Lunghezze",
+ "globals.terms.google": "Google",
+ "globals.terms.microsoft": "Microsoft",
+ "globals.terms.reconnect": "Riconnetti",
+ "globals.terms.protocol": "protocollo",
"globals.terms.size": "Dimensione | Dimensioni",
"globals.terms.upload": "Caricamento | Caricamenti",
"globals.terms.download": "Scarica | Scaricamenti",
@@ -189,6 +205,9 @@
"globals.terms.recipient": "Destinatario | Destinatari",
"globals.terms.tls": "TLS | TLS",
"globals.terms.credential": "Credenziale | Credenziali",
+ "globals.terms.tenantID": "ID tenant",
+ "globals.terms.copy": "Copia",
+ "globals.messages.markAsUnread": "Segna come non letto",
"globals.messages.welcomeToLibredesk": "Benvenuti su Libredesk",
"globals.messages.invalid": "{name} Non Valido",
"globals.messages.custom": "{name} Personalizzato",
@@ -238,6 +257,9 @@
"globals.messages.revokedSuccessfully": "{name} revocato correttamente",
"globals.messages.errorRevoking": "Errore nella revoca di {name}",
"globals.messages.generatedSuccessfully": "{name} generato correttamente",
+ "globals.messages.connectedSuccessfully": "{name} connesso correttamente",
+ "globals.messages.reconnectedSuccessfully": "{name} riconnesso correttamente",
+ "globals.messages.errorConnecting": "Errore durante la connessione di {name}",
"globals.messages.generate": "Generazione {name}",
"globals.messages.generated": "{name} generato",
"globals.messages.regenerate": "Rigenera",
@@ -252,6 +274,8 @@
"globals.messages.adding": "Aggiunta {name}",
"globals.messages.starting": "Inizio {name}",
"globals.messages.all": "Tutti {name}",
+ "globals.messages.deleteAll": "Elimina tutto",
+ "globals.messages.markAllAsRead": "Segna tutto come letto",
"globals.messages.denied": "{name} negato",
"globals.messages.noResults": "Nessun {name} trovato",
"globals.messages.enter": "Inserisci {name}",
@@ -259,6 +283,7 @@
"globals.messages.no": "No {name}",
"globals.messages.select": "Seleziona {name}",
"globals.messages.copied": "Copiato negli appunti",
+ "globals.messages.errorCopying": "Errore durante la copia negli appunti",
"globals.messages.search": "Cerca {name}",
"globals.messages.type": "Tipo {name}",
"globals.messages.typeOf": "Tipo di {name}",
@@ -294,6 +319,8 @@
"globals.messages.saveChanges": "Salva le modifiche",
"globals.messages.cancel": "Annulla",
"globals.messages.submit": "Invia",
+ "globals.messages.continue": "Continua",
+ "globals.messages.connecting": "Connessione in corso...",
"globals.messages.send": "Invia {name}",
"globals.messages.update": "Aggiorna {name}",
"globals.messages.setUp": "Imposta",
@@ -306,6 +333,7 @@
"globals.messages.upload": "Caricamento",
"globals.messages.back": "Indietro",
"globals.messages.close": "Chiudi",
+ "globals.messages.import": "Importa {name}",
"globals.messages.apply": "Applica {name}",
"globals.messages.reset": "Resetta {name}",
"globals.messages.lastNItems": "Ultimo {n} {name} | Ultimi {n} {name}",
@@ -332,8 +360,13 @@
"user.errorGeneratingPasswordToken": "Errore nella generazione del token password",
"media.fileSizeTooLarge": "Dimensione del file troppo grande, carica un file meno di {size} ",
"media.fileTypeNotAllowed": "Tipo file non consentito",
+ "media.fileEmpty": "Questo file è di 0 byte, quindi non verrà allegato.",
+ "media.invalidOrExpiredURL": "URL del contenuto multimediale non valido o scaduto",
"inbox.emptyIMAP": "Configurazione IMAP vuota",
"inbox.emptySMTP": "Configurazione SMTP vuota",
+ "inbox.oauthAlreadyExists": "Esiste già una casella di posta con questa email. Usa Riconnetti per aggiornare le credenziali.",
+ "inbox.oauthNotFound": "Nessuna casella di posta trovata con questa email da riconnettere.",
+ "inbox.oauthEmailMismatch": "L’email autorizzata non corrisponde a questa casella di posta. Autorizza con l’account corretto.",
"template.defaultTemplateAlreadyExists": "Il modello predefinito esiste già",
"template.cannotDeleteBuiltInTemplate": "Impossibile eliminare il modello integrato",
"role.invalidPermission": "Permesso non valido {name}",
@@ -409,10 +442,10 @@
"admin.general.logoURL": "URL del logo",
"admin.general.logoURL.description": "URL del logo per l'applicazione.",
"admin.general.logoURL.valid": "L'URL del logo dovrebbe essere un URL valido",
- "admin.general.maxAllowedFileUploadSize": "Dimensione Massima Di Caricamento File Consentita",
+ "admin.general.maxAllowedFileUploadSize": "Dimensione massima consentita per il caricamento dei file",
"admin.general.maxAllowedFileUploadSize.description": "Dimensione massima di caricamento file consentita in MB.",
"admin.general.maxAllowedFileUploadSize.valid": "La dimensione massima consentita per il caricamento dei file dovrebbe essere compresa tra 1 e 500 MB",
- "admin.general.allowedFileUploadExtensions": "Estensioni consentite per il caricamento dei file",
+ "admin.general.allowedFileUploadExtensions": "Estensioni di file consentite per il caricamento",
"admin.general.allowedFileUploadExtensions.description": "Usa `*` per consentire tutti i tipi di file. Per esempio: `jpg, png, pdf`",
"admin.businessHours.unauthorized": "Non hai il permesso di visualizzare l'orario lavorativo.",
"admin.businessHours.setBusinessHours": "Imposta l'orario di lavoro",
@@ -481,6 +514,25 @@
"admin.inbox.chooseChannel": "Scegli un canale",
"admin.inbox.configureChannel": "Configura canale",
"admin.inbox.createEmailInbox": "Crea casella email",
+ "admin.inbox.oauth.chooseSetupMethod": "Scegli il metodo di configurazione",
+ "admin.inbox.oauth.selectConnectionMethod": "Seleziona come vuoi collegare il tuo account email",
+ "admin.inbox.oauth.googleDescription": "Connetti con Google Workspace o Gmail",
+ "admin.inbox.oauth.microsoftDescription": "Connetti con Microsoft 365 o Outlook",
+ "admin.inbox.oauth.otherProvider": "Altro provider",
+ "admin.inbox.oauth.otherProviderDescription": "Configura manualmente IMAP e SMTP",
+ "admin.inbox.oauth.connectedVia": "Connesso tramite OAuth - {provider}",
+ "admin.inbox.oauth.connectAccount": "Connetti account {provider}",
+ "admin.inbox.oauth.followSteps": "Segui i passaggi seguenti per collegare il tuo account email",
+ "admin.inbox.oauth.step1CreateApp": "1. Crea un'app OAuth a",
+ "admin.inbox.oauth.googleCloudConsole": "Google Cloud Console",
+ "admin.inbox.oauth.microsoftAzurePortal": "Portale Microsoft Azure",
+ "admin.inbox.oauth.step2AddCallback": "2. Aggiungi questo URL di callback:",
+ "admin.inbox.oauth.step3EnterCredentials": "3. Inserisci le tue credenziali qui sotto:",
+ "admin.inbox.oauth.enterClientID": "Inserisci l’ID client OAuth",
+ "admin.inbox.oauth.enterClientSecret": "Inserisci il tuo segreto client OAuth",
+ "admin.inbox.oauth.clientIDSecretRequired": "Fornisci sia l’ID client che il client secret",
+ "admin.inbox.oauth.reconnectAccount": "Riconnetti l’account {provider}",
+ "admin.inbox.oauth.reconnectDescription": "Reinserisci le tue credenziali per aggiornare la connessione OAuth",
"admin.agent.deleteConfirmation": "Questo eliminerà definitivamente l'agente. Considera invece di disabilitare l'account.",
"admin.agent.apiKey.description": "Genera chiavi API per questo agente per accedere a libredesk programmaticamente.",
"admin.agent.apiKey.noKey": "Nessuna chiave API è stata generata per questo agente.",
@@ -494,6 +546,7 @@
"admin.role.conversations.readAll": "Mostra tutte le conversazioni",
"admin.role.conversations.readUnassigned": "Visualizza tutte le conversazioni non assegnate",
"admin.role.conversations.readTeamInbox": "Visualizza le conversazioni nella posta in arrivo del team",
+ "admin.role.conversations.readTeamAll": "Visualizza le conversazioni della tua squadra",
"admin.role.conversations.updateUserAssignee": "Assegna conversazioni ai team",
"admin.role.conversations.updateTeamAssignee": "Assegna conversazioni ai team",
"admin.role.conversations.updatePriority": "Cambia priorità conversazione",
@@ -503,6 +556,7 @@
"admin.role.messages.write": "Invia messaggi nelle conversazioni",
"admin.role.messages.writeAsContact": "Invia messaggi come contatto",
"admin.role.view.manage": "Crea e gestisci le viste delle conversazioni",
+ "admin.role.sharedViews.manage": "Gestisci Viste Condivise",
"admin.role.generalSettings.manage": "Gestisci impostazioni generali",
"admin.role.notificationSettings.manage": "Gestisci Impostazioni Di Notifica",
"admin.role.status.manage": "Gestisci Stato Conversazione",
@@ -559,6 +613,7 @@
"admin.template.makeSureTemplateHasContent": "Assicurati che nel modello appaia {content} una volta soltanto.",
"admin.template.onlyOneDefaultOutgoingTemplate": "Puoi avere un solo modello predefinito di email in uscita.",
"admin.sso.setThisUrlForCallback": "Imposta questo URI per la callback.",
+ "admin.sso.logoURLDescription": "URL del logo personalizzato da visualizzare nella pagina di login.",
"admin.customAttributes.regex.description": "Regex per convalidare il valore di questo attributo personalizzato. Lasciare vuoto per saltare la convalida.",
"admin.customAttributes.regexHint.description": "Suggerimento modello Regex.",
"admin.customAttributes.keyNotAllowed": "La chiave fornita non è consentita in quanto è in conflitto con gli attributi predefiniti. Si prega di utilizzare una chiave diversa.",
@@ -586,6 +641,29 @@
"report.sla.resolutionMet": "Orario di risoluzione rispettato",
"report.sla.resolutionBreached": "Risoluzione violata",
"report.sla.avgResolution": "Orario di risoluzione medio",
+ "report.sla.compliance": "Conformità",
+ "report.sla.met": "Incontrato",
+ "report.sla.breached": "Violazione",
+ "report.sla.firstResponse": "Prima Risposta",
+ "report.sla.nextResponse": "Risposta Successiva",
+ "report.sla.resolution": "Risoluzione",
+ "report.openConversations": "Apri conversazione",
+ "report.agentStatus": "Stato dell’agente",
+ "report.csat.title": "Soddisfazione del cliente",
+ "report.csat.cardTitle": "Soddisfazione del cliente (ultimo {days} giorni)",
+ "report.csat.avgRating": "Valutazione media",
+ "report.csat.responseRate": "Tasso di risposta",
+ "report.csat.responses": "Risposte",
+ "report.messages.title": "Volume Messaggio",
+ "report.messages.cardTitle": "Volume messaggio (ultimo {days} giorni)",
+ "report.messages.total": "Totale",
+ "report.messages.incoming": "In arrivo",
+ "report.messages.outgoing": "In uscita",
+ "report.messages.perConversation": "Per conversazione",
+ "report.tags.title": "Distribuzione dei tag",
+ "report.tags.cardTitle": "Distribuzione dei tag (ultimi {days} giorni)",
+ "report.tags.tagged": "Taggato",
+ "report.tags.topTags": "Tag principali",
"search.noResultsForQuery": "Nessun risultato trovato per la query `{query}`. Prova un termine di ricerca diverso.",
"search.minQueryLength": "Per favore inserisci almeno {length} caratteri per la ricerca.",
"search.searchBy": "Cerca per numero di riferimento, indirizzo email di contatto o messaggi nelle conversazioni.",
@@ -646,5 +724,10 @@
"contact.notes.help": "Aggiungi una nota per questo contatto per tenere traccia di importanti informazioni e conversazioni.",
"setup.completeYourSetup": "Completa la tua configurazione",
"setup.createFirstInbox": "Crea la tua prima casella di posta",
- "setup.inviteTeammates": "Invita colleghi"
+ "setup.inviteTeammates": "Invita colleghi",
+ "importer.requiredCSVFormat": "Formato CSV richiesto",
+ "importer.importCompleted": "Importazione completata: {success} di {total} riuscite, {errors} non riuscite",
+ "importer.csvMustContainHeadersAndData": "Il CSV deve contenere intestazioni e almeno una riga di dati",
+ "importer.importAlreadyInProgress": "Importazione già in corso",
+ "importer.agentCaseSensitiveNote": "Ruoli e team devono corrispondere esattamente (distinzione tra maiuscole e minuscole)"
}
\ No newline at end of file
diff --git a/i18n/ja.json b/i18n/ja.json
index f8ec5c31..e04ab6a4 100644
--- a/i18n/ja.json
+++ b/i18n/ja.json
@@ -8,6 +8,7 @@
"globals.terms.message": "メッセージ",
"globals.terms.activityMessage": "アクティビティメッセージ",
"globals.terms.account": "アカウント",
+ "globals.terms.authorization": "認証",
"globals.terms.conversation": "会話",
"globals.terms.provider": "プロバイダー",
"globals.terms.state": "都道府県",
@@ -17,9 +18,18 @@
"globals.terms.permission": "アクセス許可",
"globals.terms.request": "リクエスト",
"globals.terms.file": "ファイル",
+ "globals.terms.csvFile": "CSV ファイル",
"globals.terms.actor": "操作担当者",
"globals.terms.page": "ページ",
+ "globals.terms.log": "ログ",
"globals.terms.activityLog": "アクティビティログ",
+ "activityLog.type.agentLogin": "担当者がログインしました",
+ "activityLog.type.agentLogout": "担当者がログアウトしました",
+ "activityLog.type.agentAway": "担当者が離席しました",
+ "activityLog.type.agentAwayReassigned": "担当者が離席し再割り当てをしました",
+ "activityLog.type.agentOnline": "担当者がオンラインになりました",
+ "activityLog.type.agentPasswordSet": "担当者がパスワードをセットしました",
+ "activityLog.type.agentRolePermissionsChanged": "担当者のロール権限が変更されました",
"globals.terms.name": "名前",
"globals.terms.image": "画像",
"globals.terms.thumbnail": "サムネイル",
@@ -54,6 +64,7 @@
"globals.terms.role": "役割",
"globals.terms.avatar": "アバター",
"globals.terms.view": "表示",
+ "globals.terms.sharedView": "共有ビュー",
"globals.terms.email": "Eメール",
"globals.terms.condition": "条件",
"globals.terms.sso": "SSO",
@@ -70,7 +81,7 @@
"globals.terms.meta": "メタ",
"globals.terms.online": "オンライン",
"globals.terms.offline": "オフライン",
- "globals.terms.away": "不在",
+ "globals.terms.away": "離席中",
"globals.terms.admin": "管理者",
"globals.terms.customAttribute": "カスタム属性",
"globals.terms.attribute": "属性",
@@ -82,6 +93,7 @@
"globals.terms.open": "オープン",
"globals.terms.awaitingResponse": "回答待ち",
"globals.terms.unassigned": "未割り当て",
+ "globals.terms.mention": "あなた宛て",
"globals.terms.pending": "保留中",
"globals.terms.active": "有効",
"globals.terms.url": "URL",
@@ -126,6 +138,10 @@
"globals.terms.min": "最小",
"globals.terms.max": "最大",
"globals.terms.length": "長さ",
+ "globals.terms.google": "Google",
+ "globals.terms.microsoft": "Microsoft",
+ "globals.terms.reconnect": "再接続",
+ "globals.terms.protocol": "プロトコル",
"globals.terms.size": "サイズ",
"globals.terms.upload": "アップロード",
"globals.terms.download": "ダウンロード",
@@ -140,7 +156,7 @@
"globals.terms.regex": "正規表現",
"globals.terms.appliesTo": "適用先",
"globals.terms.createdOn": "作成日時",
- "globals.terms.awayReassigning": "不在時の再割り当て",
+ "globals.terms.awayReassigning": "離席と再割り当て",
"globals.terms.availabilityStatus": "対応状況",
"globals.terms.lastActive": "最終活動日時",
"globals.terms.lastLogin": "最終ログイン",
@@ -189,6 +205,9 @@
"globals.terms.recipient": "受信者",
"globals.terms.tls": "TLS",
"globals.terms.credential": "認証情報",
+ "globals.terms.tenantID": "テナントID",
+ "globals.terms.copy": "コピー",
+ "globals.messages.markAsUnread": "未読にする",
"globals.messages.welcomeToLibredesk": "Libredesk へようこそ",
"globals.messages.invalid": "無効な {name}",
"globals.messages.custom": "カスタム {name}",
@@ -238,20 +257,25 @@
"globals.messages.revokedSuccessfully": "{name} は正常に取り消されました",
"globals.messages.errorRevoking": "{name} の取り消し中にエラーが発生しました",
"globals.messages.generatedSuccessfully": "{name} は正常に生成されました",
+ "globals.messages.connectedSuccessfully": "{name} は正常に接続されました",
+ "globals.messages.reconnectedSuccessfully": "{name} は正常に再接続されました",
+ "globals.messages.errorConnecting": "{name} の接続中にエラーが発生しました",
"globals.messages.generate": "{name} を生成",
"globals.messages.generated": "{name} が生成されました",
"globals.messages.regenerate": "再生成",
"globals.messages.revoke": "取り消す",
"globals.messages.lastUsed": "最終使用日時",
"globals.messages.pageTooLarge": "ページサイズが大きすぎます。 {max} 以下にしてください",
- "globals.messages.edit": "{name} を編集",
- "globals.messages.delete": "{name} を削除",
+ "globals.messages.edit": "編集 {name}",
+ "globals.messages.delete": "削除 {name}",
"globals.messages.create": "{name} を作成",
"globals.messages.new": "{name} を新規作成",
"globals.messages.add": "{name} を追加",
"globals.messages.adding": "{name} を追加",
"globals.messages.starting": "{name} を開始",
"globals.messages.all": "すべての {name}",
+ "globals.messages.deleteAll": "すべて削除",
+ "globals.messages.markAllAsRead": "すべて既読にする",
"globals.messages.denied": "{name} を拒否しました",
"globals.messages.noResults": "{name} が見つかりませんでした",
"globals.messages.enter": "{name} を入力します",
@@ -259,6 +283,7 @@
"globals.messages.no": "いいえ、{name}",
"globals.messages.select": "{name} を選択",
"globals.messages.copied": "クリップボードにコピーしました",
+ "globals.messages.errorCopying": "クリップボードへのコピーに失敗しました",
"globals.messages.search": "{name} を検索",
"globals.messages.type": "{name} のタイプ",
"globals.messages.typeOf": "{name} のタイプ",
@@ -282,7 +307,7 @@
"globals.messages.selectAFutureTime": "未来の時刻を選択してください",
"globals.messages.assign": "{name} を割り当て",
"globals.messages.set": "{name} を設定",
- "globals.messages.remove": "{name} を削除",
+ "globals.messages.remove": "削除 {name}",
"globals.messages.deletionConfirmation": "この操作は元に戻せません。この操作は {name} を完全に削除します。",
"globals.messages.startTypingToSearch": "入力を開始して検索…",
"globals.messages.goHourMinuteDuration": "無効な期間形式です。数字の後に「h」(時間)または「m」(分)を付けて入力してください。",
@@ -294,6 +319,8 @@
"globals.messages.saveChanges": "変更を保存",
"globals.messages.cancel": "キャンセル",
"globals.messages.submit": "送信",
+ "globals.messages.continue": "続ける",
+ "globals.messages.connecting": "接続中…",
"globals.messages.send": "{name} を送信",
"globals.messages.update": "{name} を更新",
"globals.messages.setUp": "設定",
@@ -306,6 +333,7 @@
"globals.messages.upload": "アップロード",
"globals.messages.back": "戻る",
"globals.messages.close": "クローズ",
+ "globals.messages.import": "{name} をインポート",
"globals.messages.apply": "{name} を適用",
"globals.messages.reset": "{name} をリセット",
"globals.messages.lastNItems": "直近の {n} {name}",
@@ -333,8 +361,12 @@
"media.fileSizeTooLarge": "ファイルサイズが大きすぎます。 {size} 未満のファイルをアップロードしてください ",
"media.fileTypeNotAllowed": "ファイルタイプが許可されていません",
"media.fileEmpty": "このファイルは 0 バイトなので、添付されません。",
+ "media.invalidOrExpiredURL": "無効または期限切れのメディア URL",
"inbox.emptyIMAP": "IMAP の設定が空です",
"inbox.emptySMTP": "SMTPの設定が空です",
+ "inbox.oauthAlreadyExists": "このメールの受信トレイは既に存在します。認証情報を更新するには再接続してください。",
+ "inbox.oauthNotFound": "このメールアドレスで再接続する受信トレイが見つかりません。",
+ "inbox.oauthEmailMismatch": "承認されたメールアドレスがこの受信トレイと一致しません。正しいアカウントで認証してください。",
"template.defaultTemplateAlreadyExists": "デフォルトのテンプレートは既に存在します",
"template.cannotDeleteBuiltInTemplate": "組み込みテンプレートは削除できません",
"role.invalidPermission": "{name} の権限が無効です",
@@ -346,7 +378,7 @@
"macro.couldNotApply": "マクロを適用できませんでした",
"macro.partiallyApplied": "マクロは部分的に適用されました",
"macro.applied": "マクロが適用されました",
- "sla.firstResponseTimeAfterResolution": "初回対応時間は解決時間より後には設定できません",
+ "sla.firstResponseTimeAfterResolution": "初回応答時間は解決時間より後には設定できません",
"conversationStatus.alreadyInUse": "このステータスは使用中のため削除できません。削除する前に、すべての会話からこのステータスを削除してください",
"conversationStatus.cannotUpdateDefault": "デフォルトの会話ステータスは更新できません",
"csat.alreadySubmitted": "CSAT はすでに送信されました",
@@ -411,7 +443,7 @@
"admin.general.logoURL.description": "アプリのロゴURLです。",
"admin.general.logoURL.valid": "ロゴのURLは有効なURLである必要があります",
"admin.general.maxAllowedFileUploadSize": "許可されたファイルの最大アップロードサイズ",
- "admin.general.maxAllowedFileUploadSize.description": "アップロード可能な最大ファイルサイズ(MB単位)。",
+ "admin.general.maxAllowedFileUploadSize.description": "許可されているファイルの最大アップロードサイズ(MB)。",
"admin.general.maxAllowedFileUploadSize.valid": "アップロード可能な最大ファイルサイズは 1~500 MB の間で指定してください",
"admin.general.allowedFileUploadExtensions": "アップロード可能なファイル拡張子",
"admin.general.allowedFileUploadExtensions.description": "すべてのファイルタイプを許可するには、`*` を使用します。例: `jpg, png, pdf`",
@@ -423,34 +455,34 @@
"admin.businessHours.openClose.required": "開始時間と終了時間は必須です",
"admin.sla.name.valid": "SLAポリシー名は 1~255 文字の間で指定してください",
"admin.sla.description.valid": "SLAポリシーの説明は 1~255 文字の間で指定してください",
- "admin.sla.firstResponseTime": "初回返信時間",
+ "admin.sla.firstResponseTime": "初回応答時間",
"admin.sla.resolutionTime": "解決時間",
- "admin.sla.nextResponseTime": "次回対応時間",
+ "admin.sla.nextResponseTime": "追加応答時間",
"admin.sla.alertConfiguration": "アラート設定",
"admin.sla.alertConfiguration.description": "アラートのトリガー条件と通知先を設定",
- "admin.sla.addBreachAlert": "違反アラートを追加",
+ "admin.sla.addBreachAlert": "超過アラートを追加",
"admin.sla.addWarningAlert": "警告アラートを追加",
"admin.sla.warning": "警告",
- "admin.sla.breach": "違反",
+ "admin.sla.breach": "超過",
"admin.sla.triggerTiming": "トリガータイミング",
- "admin.sla.immediatelyOnBreach": "違反発生時に即時",
+ "admin.sla.immediatelyOnBreach": "超過発生時に即時",
"admin.sla.afterSpecificDuration": "指定時間経過後",
"admin.sla.advanceWarning": "事前警告",
"admin.sla.followUpDelay": "フォローアップ遅延",
"admin.sla.alertRecipients": "アラート受信者",
"admin.sla.noAlertsConfigured": "アラートが設定されていません",
- "admin.sla.atleastOneSLATimeRequired": "「初回応答時間」「次回応答時間」「解決時間」のいずれかを設定する必要があります。",
+ "admin.sla.atleastOneSLATimeRequired": "「初回応答時間」「追加応答時間」「解決時間」のいずれかを設定する必要があります。",
"admin.conversationTags.edit.description": "タグ名を変更してください。変更が終わったら「保存」をクリックしてください。",
"admin.conversationTags.new.description": "タグ名を設定してください。設定が終わったら「保存」をクリックしてください。",
"admin.conversationTags.name.valid": "タグ名は少なくとも 3 文字必要です",
- "admin.macro.messageContent": "マクロ使用時に送信する返信(任意)",
+ "admin.macro.messageContent": "マクロを使用したときに送信される応答 (オプション)",
"admin.macro.actions": "アクション (任意)",
"admin.macro.messageOrActionRequired": "メッセージ内容または操作のいずれかを設定する必要があります",
"admin.macro.actionInvalid": "各アクションには種類と値の両方を指定する必要があります",
"admin.conversationStatus.name.description": "ステータス名を設定してください。設定が終わったら「保存」をクリックしてください。",
"admin.inbox.name.description": "受信トレイの名前。",
"admin.inbox.fromEmailAddress.placeholder": "自分の受信トレイ ",
- "admin.inbox.fromEmailAddress.description": "受信トレイの送信元メールアドレス(例:自分の受信トレイ support@example.com)",
+ "admin.inbox.fromEmailAddress.description": "受信トレイの送信元メールアドレス。 例:サポート ",
"admin.inbox.enabled.description": "受信トレイのスキャンと送信の切替。",
"admin.inbox.csatSurveys": "CSAT調査",
"admin.inbox.csatSurveys.description_1": "会話が「解決済み」にマークされたときに、顧客満足度アンケートを送信する。",
@@ -482,6 +514,25 @@
"admin.inbox.chooseChannel": "送信先のチャンネルを選択",
"admin.inbox.configureChannel": "チャンネルの設定",
"admin.inbox.createEmailInbox": "メール受信トレイを作成",
+ "admin.inbox.oauth.chooseSetupMethod": "セットアップ方法を選択",
+ "admin.inbox.oauth.selectConnectionMethod": "メールアカウントを接続する方法を選択してください",
+ "admin.inbox.oauth.googleDescription": "Google ワークスペースまたは Gmail に接続",
+ "admin.inbox.oauth.microsoftDescription": "Microsoft 365 または Outlook と接続",
+ "admin.inbox.oauth.otherProvider": "その他のプロバイダー",
+ "admin.inbox.oauth.otherProviderDescription": "IMAP および SMTP の手動設定",
+ "admin.inbox.oauth.connectedVia": "OAuth 経由で接続 - {provider}",
+ "admin.inbox.oauth.connectAccount": "{provider} アカウントと接続",
+ "admin.inbox.oauth.followSteps": "以下の手順に従ってメールアカウントを接続してください",
+ "admin.inbox.oauth.step1CreateApp": "1. OAuth アプリを作成する",
+ "admin.inbox.oauth.googleCloudConsole": "Google クラウドコンソール",
+ "admin.inbox.oauth.microsoftAzurePortal": "Microsoft Azure ポータル",
+ "admin.inbox.oauth.step2AddCallback": "2. このコールバックURLを追加:",
+ "admin.inbox.oauth.step3EnterCredentials": "3. あなたの資格情報を以下に入力してください:",
+ "admin.inbox.oauth.enterClientID": "OAuth クライアント ID を入力してください",
+ "admin.inbox.oauth.enterClientSecret": "OAuthクライアントシークレットを入力してください",
+ "admin.inbox.oauth.clientIDSecretRequired": "クライアントIDとクライアントシークレットの両方を入力してください",
+ "admin.inbox.oauth.reconnectAccount": "{provider} アカウントに再接続する",
+ "admin.inbox.oauth.reconnectDescription": "OAuth接続を更新するには資格情報を再入力してください",
"admin.agent.deleteConfirmation": "これによりエージェントが完全に削除されます。代わりにアカウントを無効化することを検討してください。",
"admin.agent.apiKey.description": "このエージェントが LibreDesk にプログラムからアクセスできるように API キーを生成します。",
"admin.agent.apiKey.noKey": "この担当者にはまだ API キーが生成されていません。",
@@ -495,6 +546,7 @@
"admin.role.conversations.readAll": "すべての会話を表示",
"admin.role.conversations.readUnassigned": "割り当てられていないすべての会話を表示",
"admin.role.conversations.readTeamInbox": "チームの受信トレイ内の会話を表示",
+ "admin.role.conversations.readTeamAll": "チームの会話を表示",
"admin.role.conversations.updateUserAssignee": "ユーザーに会話を割り当てる",
"admin.role.conversations.updateTeamAssignee": "チームに会話を割り当てる",
"admin.role.conversations.updatePriority": "会話の優先度を変更",
@@ -504,6 +556,7 @@
"admin.role.messages.write": "会話内でメッセージを送信",
"admin.role.messages.writeAsContact": "連絡先としてメッセージを送信",
"admin.role.view.manage": "会話ビューを作成・管理",
+ "admin.role.sharedViews.manage": "共有ビューの管理",
"admin.role.generalSettings.manage": "一般設定を管理",
"admin.role.notificationSettings.manage": "通知設定を管理",
"admin.role.status.manage": "会話ステータスの管理",
@@ -560,6 +613,7 @@
"admin.template.makeSureTemplateHasContent": "テンプレート内に {content} は1回だけ含めるようにしてください。",
"admin.template.onlyOneDefaultOutgoingTemplate": "デフォルトの送信メールテンプレートは1つのみ設定できます。",
"admin.sso.setThisUrlForCallback": "コールバック用の URI を設定します。",
+ "admin.sso.logoURLDescription": "ログインページに表示するカスタムロゴURLです。",
"admin.customAttributes.regex.description": "このカスタム属性の値を検証する正規表現。検証を行わない場合は空欄にしてください。",
"admin.customAttributes.regexHint.description": "正規表現パターンのヒント。",
"admin.customAttributes.keyNotAllowed": "指定したキーはデフォルト属性と重複しているため使用できません。別のキーを使用してください。",
@@ -577,16 +631,39 @@
"report.chart.newConversations": "新しい会話",
"report.chart.resolvedConversations": "解決済みの会話",
"report.chart.title": "会話の傾向",
- "report.sla.cardTitle": "SLA 達成状況(過去 {days} 日間)",
+ "report.sla.cardTitle": "SLA 達成状況(直近 {days} 日間)",
"report.sla.firstRespMet": "初回応答達成",
- "report.sla.firstRespBreached": "初回応答未達",
+ "report.sla.firstRespBreached": "初回応答超過",
"report.sla.avgFirstResp": "平均初回応答時間",
- "report.sla.nextRespMet": "次回応答達成",
- "report.sla.nextRespBreached": "次回応答未達",
- "report.sla.avgNextResp": "平均次回応答時間",
+ "report.sla.nextRespMet": "追加応答達成",
+ "report.sla.nextRespBreached": "追加応答超過",
+ "report.sla.avgNextResp": "平均追加応答時間",
"report.sla.resolutionMet": "解決達成",
- "report.sla.resolutionBreached": "解決未達",
+ "report.sla.resolutionBreached": "解決超過",
"report.sla.avgResolution": "平均解決時間",
+ "report.sla.compliance": "コンプライアンス",
+ "report.sla.met": "達成",
+ "report.sla.breached": "違反",
+ "report.sla.firstResponse": "初回応答",
+ "report.sla.nextResponse": "追加応答",
+ "report.sla.resolution": "解決",
+ "report.openConversations": "会話を開く",
+ "report.agentStatus": "担当者の状態",
+ "report.csat.title": "顧客満足度",
+ "report.csat.cardTitle": "顧客満足度(直近 {days} 日間)",
+ "report.csat.avgRating": "平均評価",
+ "report.csat.responseRate": "応答率",
+ "report.csat.responses": "応答",
+ "report.messages.title": "メッセージ数",
+ "report.messages.cardTitle": "メッセージの数(直近 {days} 日間)",
+ "report.messages.total": "合計",
+ "report.messages.incoming": "受信",
+ "report.messages.outgoing": "送信",
+ "report.messages.perConversation": "会話ごと",
+ "report.tags.title": "タグの分布",
+ "report.tags.cardTitle": "タグの分布(直近 {days} 日間)",
+ "report.tags.tagged": "タグを付けました",
+ "report.tags.topTags": "トップタグ",
"search.noResultsForQuery": "「{query}」に一致する結果は見つかりませんでした。別の検索語を試してください。",
"search.minQueryLength": " 検索するには、少なくとも {length} 文字を入力してください。",
"search.searchBy": "参照番号、連絡先のメールアドレス、または会話内のメッセージで検索できます。",
@@ -617,7 +694,7 @@
"conversation.sort.oldestActivity": "最も古いアクティビティ",
"conversation.sort.newestActivity": "最新のアクティビティ",
"conversation.sort.startedFirst": "最初に開始",
- "conversation.sort.startedLast": "最後に開始",
+ "conversation.sort.startedLast": "開始時刻",
"conversation.sort.waitingLongest": "待機時間最長",
"conversation.sort.nextSLATarget": "次のSLA目標",
"conversation.sort.priorityFirst": "優先度優先",
@@ -647,5 +724,10 @@
"contact.notes.help": "この連絡先の重要な情報や会話を記録するために、ノートを追加してください。",
"setup.completeYourSetup": "セットアップを完了してください",
"setup.createFirstInbox": "最初の受信トレイを作成してください",
- "setup.inviteTeammates": "チームメンバーを招待"
-}
+ "setup.inviteTeammates": "チームメンバーを招待",
+ "importer.requiredCSVFormat": "必須のCSV形式",
+ "importer.importCompleted": "インポートが完了しました: {total} 件のうち {success} 件が成功しました。 {errors} 件が失敗しました",
+ "importer.csvMustContainHeadersAndData": "CSVにはヘッダーと少なくとも1つのデータ行が含まれている必要があります",
+ "importer.importAlreadyInProgress": "インポートはすでに進行中です",
+ "importer.agentCaseSensitiveNote": "ロールとチームは正確に一致 (大文字と小文字を区別)する必要があります"
+}
\ No newline at end of file
diff --git a/i18n/mr.json b/i18n/mr.json
index 1ec842ea..4e6a3ae4 100644
--- a/i18n/mr.json
+++ b/i18n/mr.json
@@ -35,7 +35,6 @@
"globals.terms.dashboard": "डॅशबोर्ड | डॅशबोर्ड्स",
"globals.terms.tag": "टॅग | टॅग्ज",
"globals.terms.sla": "SLA | SLAs",
- "globals.terms.slaPolicy": "SLA धोरण | SLA धोरणे",
"globals.terms.csatSurvey": "CSAT सर्व्हे | CSAT सर्व्हे",
"globals.terms.csatResponse": "CSAT प्रतिसाद | CSAT प्रतिसाद",
"globals.terms.inbox": "इनबॉक्स | इनबॉक्स",
@@ -279,10 +278,8 @@
"admin.general.logoURL": "लोगो URL",
"admin.general.logoURL.description": "अॅपसाठी लोगो URL.",
"admin.general.logoURL.valid": "लोगो URL वैध असावा",
- "admin.general.maxAllowedFileUploadSize": "कमाल फाइल अपलोड आकार",
"admin.general.maxAllowedFileUploadSize.description": "MB मध्ये कमाल फाइल अपलोड आकार.",
"admin.general.maxAllowedFileUploadSize.valid": "कमाल फाइल आकार 1 ते 500 MB दरम्यान असावा",
- "admin.general.allowedFileUploadExtensions": "परवानगी असलेली फाइल एक्सटेंशन्स",
"admin.businessHours.unauthorized": "व्यवसाय तास पाहण्याची परवानगी नाही.",
"admin.businessHours.setBusinessHours": "व्यवसाय तास सेट करा",
"admin.businessHours.customBusinessHours": "सानुकूल व्यवसाय तास",
diff --git a/internal/auth/auth.go b/internal/auth/auth.go
index 7c250281..214fb69f 100644
--- a/internal/auth/auth.go
+++ b/internal/auth/auth.go
@@ -101,6 +101,7 @@ func New(cfg Config, i18n *i18n.I18n, rd *redis.Client, logger *logf.Logger) (*A
})
st := sessredisstore.New(context.TODO(), rd)
+ st.SetTTL(time.Hour*9, true)
sess.UseStore(st)
sess.SetCookieHooks(simpleSessGetCookieCB, simpleSessSetCookieCB)
diff --git a/internal/inbox/channel/email/email.go b/internal/inbox/channel/email/email.go
index 53eed3ce..8b437aa3 100644
--- a/internal/inbox/channel/email/email.go
+++ b/internal/inbox/channel/email/email.go
@@ -33,6 +33,7 @@ type Email struct {
headers map[string]string
lo *logf.Logger
from string
+ enablePlusAddressing bool
messageStore inbox.MessageStore
userStore inbox.UserStore
wg sync.WaitGroup
@@ -77,6 +78,7 @@ func New(store inbox.MessageStore, userStore inbox.UserStore, opts Opts) (*Email
userStore: userStore,
oauth: opts.Config.OAuth,
authType: opts.Config.AuthType,
+ enablePlusAddressing: opts.Config.EnablePlusAddressing,
tokenRefreshCallback: opts.TokenRefreshCallback,
}
return e, nil
@@ -124,11 +126,12 @@ func (e *Email) getCurrentConfig() models.Config {
e.oauthMu.RUnlock()
return models.Config{
- SMTP: e.smtpCfg,
- IMAP: e.imapCfg,
- From: e.from,
- OAuth: oauth,
- AuthType: e.authType,
+ SMTP: e.smtpCfg,
+ IMAP: e.imapCfg,
+ From: e.from,
+ OAuth: oauth,
+ AuthType: e.authType,
+ EnablePlusAddressing: e.enablePlusAddressing,
}
}
diff --git a/internal/inbox/channel/email/smtp.go b/internal/inbox/channel/email/smtp.go
index 20f11fe2..588fcc99 100644
--- a/internal/inbox/channel/email/smtp.go
+++ b/internal/inbox/channel/email/smtp.go
@@ -180,9 +180,9 @@ func (e *Email) Send(m models.OutboundMessage) error {
}
email.Headers.Set(headerLibredeskLoopPrevention, emailAddress)
- // Set Reply-To with plus-addressing for conversation matching
+ // Set Reply-To with plus-addressing for conversation matching (if enabled)
// e.g., support@company.com → support+conv-{uuid}@company.com
- if m.ConversationUUID != "" {
+ if e.enablePlusAddressing && m.ConversationUUID != "" {
replyToAddr := buildPlusAddress(emailAddress, m.ConversationUUID)
email.Headers.Set("Reply-To", replyToAddr)
e.lo.Debug("Reply-To header set with plus-addressing", "reply_to", replyToAddr)
diff --git a/internal/inbox/inbox.go b/internal/inbox/inbox.go
index 642ff887..22939ea8 100644
--- a/internal/inbox/inbox.go
+++ b/internal/inbox/inbox.go
@@ -332,16 +332,18 @@ func (m *Manager) Update(id int, inbox imodels.Inbox) (imodels.Inbox, error) {
switch current.Channel {
case "email":
var currentCfg struct {
- AuthType string `json:"auth_type"`
- OAuth map[string]string `json:"oauth"`
- IMAP []map[string]interface{} `json:"imap"`
- SMTP []map[string]interface{} `json:"smtp"`
+ AuthType string `json:"auth_type"`
+ OAuth map[string]string `json:"oauth"`
+ IMAP []map[string]any `json:"imap"`
+ SMTP []map[string]any `json:"smtp"`
+ EnablePlusAddressing bool `json:"enable_plus_addressing"`
}
var updateCfg struct {
- AuthType string `json:"auth_type"`
- OAuth map[string]string `json:"oauth"`
- IMAP []map[string]interface{} `json:"imap"`
- SMTP []map[string]interface{} `json:"smtp"`
+ AuthType string `json:"auth_type"`
+ OAuth map[string]string `json:"oauth"`
+ IMAP []map[string]any `json:"imap"`
+ SMTP []map[string]any `json:"smtp"`
+ EnablePlusAddressing bool `json:"enable_plus_addressing"`
}
if err := json.Unmarshal(current.Config, ¤tCfg); err != nil {
@@ -541,15 +543,15 @@ func (m *Manager) encryptInboxConfig(config json.RawMessage) (json.RawMessage, e
return config, nil
}
- var cfg map[string]interface{}
+ var cfg map[string]any
if err := json.Unmarshal(config, &cfg); err != nil {
return nil, fmt.Errorf("unmarshalling config: %w", err)
}
// Encrypt SMTP passwords
- if smtpSlice, ok := cfg["smtp"].([]interface{}); ok {
+ if smtpSlice, ok := cfg["smtp"].([]any); ok {
for i, smtpItem := range smtpSlice {
- if smtpMap, ok := smtpItem.(map[string]interface{}); ok {
+ if smtpMap, ok := smtpItem.(map[string]any); ok {
if password, ok := smtpMap["password"].(string); ok && password != "" {
encrypted, err := crypto.Encrypt(password, m.encryptionKey)
if err != nil {
@@ -562,9 +564,9 @@ func (m *Manager) encryptInboxConfig(config json.RawMessage) (json.RawMessage, e
}
// Encrypt IMAP passwords
- if imapSlice, ok := cfg["imap"].([]interface{}); ok {
+ if imapSlice, ok := cfg["imap"].([]any); ok {
for i, imapItem := range imapSlice {
- if imapMap, ok := imapItem.(map[string]interface{}); ok {
+ if imapMap, ok := imapItem.(map[string]any); ok {
if password, ok := imapMap["password"].(string); ok && password != "" {
encrypted, err := crypto.Encrypt(password, m.encryptionKey)
if err != nil {
@@ -577,7 +579,7 @@ func (m *Manager) encryptInboxConfig(config json.RawMessage) (json.RawMessage, e
}
// Encrypt OAuth fields if present
- if oauthMap, ok := cfg["oauth"].(map[string]interface{}); ok {
+ if oauthMap, ok := cfg["oauth"].(map[string]any); ok {
fields := []string{"client_secret", "access_token", "refresh_token"}
for _, fieldName := range fields {
if fieldValue, ok := oauthMap[fieldName].(string); ok && fieldValue != "" {
@@ -604,15 +606,15 @@ func (m *Manager) decryptInboxConfig(config json.RawMessage) (json.RawMessage, e
return config, nil
}
- var cfg map[string]interface{}
+ var cfg map[string]any
if err := json.Unmarshal(config, &cfg); err != nil {
return nil, fmt.Errorf("unmarshalling config: %w", err)
}
// Decrypt SMTP passwords
- if smtpSlice, ok := cfg["smtp"].([]interface{}); ok {
+ if smtpSlice, ok := cfg["smtp"].([]any); ok {
for i, smtpItem := range smtpSlice {
- if smtpMap, ok := smtpItem.(map[string]interface{}); ok {
+ if smtpMap, ok := smtpItem.(map[string]any); ok {
if password, ok := smtpMap["password"].(string); ok && password != "" {
decrypted, err := crypto.Decrypt(password, m.encryptionKey)
if err != nil {
@@ -625,9 +627,9 @@ func (m *Manager) decryptInboxConfig(config json.RawMessage) (json.RawMessage, e
}
// Decrypt IMAP passwords
- if imapSlice, ok := cfg["imap"].([]interface{}); ok {
+ if imapSlice, ok := cfg["imap"].([]any); ok {
for i, imapItem := range imapSlice {
- if imapMap, ok := imapItem.(map[string]interface{}); ok {
+ if imapMap, ok := imapItem.(map[string]any); ok {
if password, ok := imapMap["password"].(string); ok && password != "" {
decrypted, err := crypto.Decrypt(password, m.encryptionKey)
if err != nil {
@@ -640,7 +642,7 @@ func (m *Manager) decryptInboxConfig(config json.RawMessage) (json.RawMessage, e
}
// Decrypt OAuth fields if present
- if oauthMap, ok := cfg["oauth"].(map[string]interface{}); ok {
+ if oauthMap, ok := cfg["oauth"].(map[string]any); ok {
fields := []string{"client_secret", "access_token", "refresh_token"}
for _, fieldName := range fields {
if fieldValue, ok := oauthMap[fieldName].(string); ok && fieldValue != "" {
diff --git a/internal/inbox/models/models.go b/internal/inbox/models/models.go
index 763fc0a8..1f30a482 100644
--- a/internal/inbox/models/models.go
+++ b/internal/inbox/models/models.go
@@ -34,11 +34,12 @@ type Inbox struct {
// Config holds the email inbox configuration with multiple SMTP servers and IMAP clients.
type Config struct {
- AuthType string `json:"auth_type"` // AuthTypePassword or AuthTypeOAuth2
- OAuth *OAuthConfig `json:"oauth"` // OAuth config when auth_type is "oauth2"
- SMTP []SMTPConfig `json:"smtp"`
- IMAP []IMAPConfig `json:"imap"`
- From string `json:"from"`
+ AuthType string `json:"auth_type"` // AuthTypePassword or AuthTypeOAuth2
+ OAuth *OAuthConfig `json:"oauth"` // OAuth config when auth_type is "oauth2"
+ SMTP []SMTPConfig `json:"smtp"`
+ IMAP []IMAPConfig `json:"imap"`
+ From string `json:"from"`
+ EnablePlusAddressing bool `json:"enable_plus_addressing"` // Enable plus-addressing in Reply-To header for conversation matching
}
// OAuthConfig holds OAuth 2.0 authentication details.
diff --git a/internal/migrations/v1.0.1.go b/internal/migrations/v1.0.1.go
new file mode 100644
index 00000000..c12ecc16
--- /dev/null
+++ b/internal/migrations/v1.0.1.go
@@ -0,0 +1,20 @@
+package migrations
+
+import (
+ "github.com/jmoiron/sqlx"
+ "github.com/knadh/koanf/v2"
+ "github.com/knadh/stuffbin"
+)
+
+// V1_0_1 updates the database schema to v1.0.1.
+func V1_0_1(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
+ // Backfill enable_plus_addressing to true for existing email inboxes
+ // that don't have this field in their config JSON.
+ _, err := db.Exec(`
+ UPDATE inboxes
+ SET config = jsonb_set(config, '{enable_plus_addressing}', 'true'::jsonb, true)
+ WHERE channel = 'email'
+ AND NOT (config ? 'enable_plus_addressing');
+ `)
+ return err
+}
diff --git a/internal/migrations/v0.12.0.go b/internal/migrations/v2.0.0.go
similarity index 97%
rename from internal/migrations/v0.12.0.go
rename to internal/migrations/v2.0.0.go
index 3e74f83f..4f83e92e 100644
--- a/internal/migrations/v0.12.0.go
+++ b/internal/migrations/v2.0.0.go
@@ -6,8 +6,8 @@ import (
"github.com/knadh/stuffbin"
)
-// V0_12_0 updates the database schema to v0.12.0 (Live Chat feature).
-func V0_12_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
+// V2_0_0 updates the database schema to v2.0.0 (Live Chat feature).
+func V2_0_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
// Add 'livechat' to the channels enum if not already present
var exists bool
err := db.Get(&exists, `