mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-11 13:28:57 +00:00
Merge branch 'main' into feat/live-chat-channel
This commit is contained in:
+2
-2
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-8
@@ -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)
|
||||
|
||||
+16
-3
@@ -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)
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+2
-2
@@ -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"
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<SelectItem value="fr">French</SelectItem>
|
||||
<SelectItem value="it">Italian</SelectItem>
|
||||
<SelectItem value="ja">Japanese</SelectItem>
|
||||
<SelectItem value="es">Spanish</SelectItem>
|
||||
<SelectItem value="mr">Marathi</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
|
||||
@@ -54,11 +54,25 @@
|
||||
<Switch :checked="componentField.modelValue" @update:checked="handleChange" />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
<p class="!mt-2 text-muted-foreground text-sm">
|
||||
<p class="!mt-2 text-muted-foreground text-xs">
|
||||
{{ $t('admin.inbox.csatSurveys.description_2') }}
|
||||
</p>
|
||||
</FormField>
|
||||
|
||||
<FormField v-if="showFormFields" v-slot="{ componentField, handleChange }" name="enable_plus_addressing">
|
||||
<FormItem class="flex flex-row items-center justify-between box p-4">
|
||||
<div class="space-y-0.5">
|
||||
<FormLabel class="text-base">{{ $t('admin.inbox.enablePlusAddressing') }}</FormLabel>
|
||||
<FormDescription>
|
||||
{{ $t('admin.inbox.enablePlusAddressing.description') }}
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch :checked="componentField.modelValue" @update:checked="handleChange" />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-if="setupMethod" v-slot="{ componentField }" name="auth_type">
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
@@ -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',
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
+42
-23
@@ -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. ",
|
||||
|
||||
@@ -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",
|
||||
|
||||
+150
@@ -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ó"
|
||||
}
|
||||
@@ -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": "ساعات کاری سفارشی",
|
||||
|
||||
+1
-4
@@ -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",
|
||||
|
||||
+87
-4
@@ -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)"
|
||||
}
|
||||
+106
-24
@@ -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": "自分の受信トレイ <support{'@'}example.com>",
|
||||
"admin.inbox.fromEmailAddress.description": "受信トレイの送信元メールアドレス(例:自分の受信トレイ support@example.com)",
|
||||
"admin.inbox.fromEmailAddress.description": "受信トレイの送信元メールアドレス。 例:サポート <support{'@'}example.com>",
|
||||
"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": "ロールとチームは正確に一致 (大文字と小文字を区別)する必要があります"
|
||||
}
|
||||
@@ -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": "सानुकूल व्यवसाय तास",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
+22
-20
@@ -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 != "" {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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, `
|
||||
Reference in New Issue
Block a user